ASPNET Core Web API項目的Hateoas實現,該項目提供了完全控制哪些鏈接,以適用於您API返回的模型。為了將不同狀態傳達給最終用戶,該庫將與授權完全集成,並允許任意條件確定是否顯示或隱藏API資源之間的Hateoas鏈接。
從nuget.org安裝軟件包
PM > Install-Package RiskFirst.Hateoas這將包括在3.0.0中介紹的依賴關係風險first.hateoas.models,以從引用linkContainer基類的彙編中刪除AspnetCore依賴項。
配置每個型號的鏈接以包含。
public class Startup
{
public void ConfigureServices ( IServicesCollection services )
{
services . AddLinks ( config =>
{
config . AddPolicy < MyModel > ( policy => {
policy . RequireSelfLink ( )
. RequireRoutedLink ( "all" , "GetAllModelsRoute" )
. RequireRoutedLink ( "delete" , "DeleteModelRoute" , x => new { id = x . Id } ) ;
} ) ;
} ) ;
}
}將ILinksService注入到任何控制器(或項目中的其他類)中,以添加指向模型的鏈接。
[ Route ( "api/[controller]" ) ]
public class MyController : Controller
{
private readonly ILinksService linksService ;
public MyController ( ILinksService linksService )
{
this . linksService = linksService ;
}
[ HttpGet ( "{id}" , Name = "GetModelRoute" ) ]
public async Task < MyModel > GetMyModel ( int id )
{
var model = await myRepository . GetMyModel ( id ) ;
await linksService . AddLinksAsync ( model ) ;
return model ;
}
[ HttpGet ( Name = "GetAllModelsRoute" ) ]
public async Task < IEnumerable < MyModel > > GetAllModels ( )
{
//... snip .. //
}
[ HttpDelete ( "{id}" , Name = "DeleteModelRoute" ) ]
public async Task < MyModel > DeleteMyModel ( int id )
{
//... snip .. //
}
}上述代碼將產生響應,如以下示例
{
"id" : 1 ,
"someOtherField" : " foo " ,
"_links" : {
"self" : {
"rel" : " MyController \ GetModelRoute " ,
"href" : " https://api.example.com/my/1 " ,
"method" : " GET "
},
"all" : {
"rel" : " MyController \ GetAllModelsRoute " ,
"href" : " https://api.example.com/my " ,
"method" : " GET "
},
"delete" : {
"rel" : " MyController \ DeleteModelRoute " ,
"href" : " https://api.example.com/my/1 " ,
"method" : " DELETE "
}
}
}或者如果您正在使用XML
<? xml version = " 1.0 " ?>
< MyModel xmlns : xsi = " http://www.w3.org/2001/XMLSchema-instance " xmlns : xsd = " http://www.w3.org/2001/XMLSchema " >
< link href = " https://api.example.com/my/1 " method = " GET " rel = " self " />
< link href = " https://api.example.com/my " method = " GET " rel = " all " />
< link href = " https://api.example.com/my/1 " method = " DELETE " rel = " delete " />
< Id >1</ Id >
< SomeOtherField >foo</ SomeOtherField >
</ MyModel >可以通過為AddPolicy提供策略名稱來指定啟動過程中模型的多個命名策略。例如,當模型是列表的一部分時,您可以擁有默認值(未命名)策略,但是當模型單獨返回時,更詳細的信息。
public class Startup
{
public void ConfigureServices ( IServicesCollection services )
{
services . AddLinks ( config =>
{
config . AddPolicy < MyModel > ( policy => {
policy . RequireRoutedLink ( "self" , "GetModelRoute" , x => new { id = x . Id } )
} ) ;
config . AddPolicy < MyModel > ( "FullInfo" , policy => {
policy . RequireSelfLink ( )
. RequireRoutedLink ( "all" , "GetAllModelsRoute" )
. RequireRoutedLink ( "parentModels" , "GetParentModelRoute" , x => new { parentId = x . ParentId } ) ;
. RequireRoutedLink ( "subModels" , "GetSubModelsRoute" , x => new { id = x . Id } ) ;
. RequireRoutedLink ( "delete" , "DeleteModelRoute" , x => new { id = x . Id } ) ;
} ) ;
} ) ;
}
}使用命名策略,可以在運行時使用AddLinksAsync的超載來應用,該委員會以策略名稱:
await linksService . AddLinksAsync ( model , "FullInfo" ) ;您還可以使用LinksAttribute標記控制器方法,以覆蓋所應用的默認策略。以下代碼將“ FullInfo”配置文件應用於返回的模型,而無需在呼叫AddLinksAsync中指定策略名稱。
[ Route ( "api/[controller]" ) ]
public class MyController : Controller
{
private readonly ILinksService linksService ;
public MyController ( ILinksService linksService )
{
this . linksService = linksService ;
}
[ HttpGet ( "{id}" , Name = "GetModelRoute" ) ]
[ Links ( Policy = "FullInfo" ) ]
public async Task < MyModel > GetMyModel ( int id )
{
var model = await myRepository . GetMyModel ( id ) ;
await linksService . AddLinksAsync ( model ) ;
return model ;
}
}實現同一件事的另一種方法是用LinksAttribute標記實際對象:
[ Links ( Policy = "FullInfo" ) ]
public class MyModel : LinkContainer
{ }
[ Route ( "api/[controller]" ) ]
public class MyController : Controller
{
private readonly ILinksService linksService ;
public MyController ( ILinksService linksService )
{
this . linksService = linksService ;
}
[ HttpGet ( "{id}" , Name = "GetModelRoute" ) ]
public async Task < MyModel > GetMyModel ( int id )
{
MyModel model = await myRepository . GetMyModel ( id ) ;
await linksService . AddLinksAsync ( model ) ;
return model ;
}
} AddLinksAsync的進一步過載,該ILinksPolicy的實例或ILinksRequirement的數組將在運行時進行評估。這應該完全控制您的API代碼中任何時候應用哪些鏈接。
不應太多需要改變Href的轉換方式,但是一個共同的要求是相對而不是絕對URI。可以在基本樣本中嘗試
services . AddLinks ( config =>
{
config . UseRelativeHrefs ( ) ;
.. .
} ) ; HREF和REL轉換都可以通過提供實現ILinkTransformation類或類型來完全控制。
services . AddLinks ( config =>
{
// supply a type implementing ILinkTransformation
config . UseHrefTransformation < MyHrefTransformation > ( ) ;
// or supply an instance
config . UseRelTransformation ( new MyRelTransformation ( ) ) ;
} ) ;另外,可以使用構建器語法配置轉換
services . AddLinks ( config =>
{
// output a uri for the rel values
config . ConfigureRelTransformation ( transform => transform . AddProtocol ( )
. AddHost ( )
. AddVirtualPath ( ctx => $ "/rel/ { ctx . LinkSpec . ControllerName } / { ctx . LinkSpec . RouteName } " ) ;
} ) ;自定義轉換的兩種方式都可以在linkConfigurationsample中看到。
您可能希望控制每個模型中包含哪些鏈接,而一個常見的要求是僅顯示當前用戶被授權的鏈接。該庫完全集成到授權管道中,並將應用您應用於鏈接操作的任何授權策略。
在鏈接上啟用授權提供AuthorizeRoute條件。
public class Startup
{
public void ConfigureServices ( IServicesCollection services )
{
services . AddLinks ( config =>
{
config . AddPolicy < MyModel > ( "FullInfo" , policy => {
policy . RequireSelfLink ( )
. RequireRoutedLink ( "all" , "GetAllModelsRoute" )
. RequireRoutedLink ( "parentModels" , "GetParentModelRoute" ,
x => new { parentId = x . ParentId } , condition => condition . AuthorizeRoute ( ) ) ;
. RequireRoutedLink ( "subModels" , "GetSubModelsRoute" ,
x => new { id = x . Id } , condition => condition . AuthorizeRoute ( ) ) ;
. RequireRoutedLink ( "delete" , "DeleteModelRoute" ,
x => new { id = x . Id } , condition => condition . AuthorizeRoute ( ) ) ;
} ) ;
} ) ;
}
}在上面的示例中, GetParentModelRoute , GetSubModelsRoute & DeleteModelRoute不會向無法訪問其授權策略定義的那些路由的用戶顯示。有關ASPNET Core WebAPI項目中有關驗證的更多信息,請參見Microsoft文檔。
與上述示例一樣,還有其他條件方法,可以使您指定策略名稱,絕對策略或一組要求。
您還可以使用Assert條件有條件地顯示基於任何布爾邏輯的鏈接。例如,有一種方法允許您將常見的分頁鏈接添加到對象的分頁結果中。如果總共只有一頁的結果,您可能會認為這是不值得的。
options . AddPolicy < IPageLinkContainer > ( policy =>
{
policy . RequireelfLink ( "all" )
. RequirePagingLinks ( condition => condition . Assert ( x => x . PageCount > 1 ) ) ;
} ) ;您可以使用LinksPolicyBuilder上的通用Requires方法來添加自己的要求。此外,您必須編寫ILinksHandler的實現來處理您的要求。例如,您可能需要某些響應要求提供返回API根文檔的鏈接。為此鏈接定義一個簡單的要求。
using RiskFirst . Hateoas ;
public class ApiRootLinkRequirement : ILinksRequirement
{
public ApiRootLinkRequirement ( )
{
}
public string Id { get ; set ; } = "root" ;
}鑑於此要求,我們需要一個課程來處理它,該課程必須實現ILinkHandler並處理您的要求。
using RiskFirst . Hateoas ;
public class ApiRootLinkHandler : LinksHandler < ApiRootLinkRequirement >
{
protected override Task HandleRequirementAsync ( LinksHandlerContext context , ApiRootLinkRequirement requirement )
{
var route = context . RouteMap . GetRoute ( "ApiRoot" ) ; // Assumes your API has a named route "ApiRoot".
context . Links . Add ( new LinkSpec ( requirement . Id , route ) ) ;
context . Handled ( requirement ) ;
return Task . CompletedTask ;
}
}最後,向您的處理程序註冊IServicesCollection並在鏈接策略中使用要求
public class Startup
{
public void ConfigureServices ( IServicesCollection services )
{
services . AddLinks ( config =>
{
config . AddPolicy < MyModel > ( policy =>
{
policy . RequireRoutedLink ( "self" , "GetModelRoute" , x => new { id = x . Id } )
. Requires < ApiRootLinkRequirement > ( ) ;
} ) ;
} ) ;
services . AddTransient < ILinksHandler , ApiRootLinkHandler > ( ) ;
}
}此示例在CustomRequirementSample中進行了演示
框架中還有許多其他部分可以通過編寫您自己的適當接口的實現並在IServicesCollection中註冊以進行依賴注入來擴展。例如,您可以通過實現自己的ILinksEvaluator來更改評估鏈接的評估和應用於鏈接容器的方式
using RiskFirst . Hateoas ;
public class Startup
{
public void ConfigureServices ( IServicesCollection services )
{
services . AddLinks ( options => {
.. .
} ) ;
services . AddTransient < ILinksEvaluator , MyLinksEvaluator > ( ) ;
}
}具有默認實現但可以替換的接口列表是:
ILinkAuthorizationService ,控制鏈接條件評估期間鏈接的授權方式。ILinksEvaluator ,在寫入返回模型之前,控制如何評估和轉換鏈接。ILinksHandlerContextFactory控制著在處理過程中通過需求處理程序傳遞的上下文的創建方式。ILinksPolicyProvider ,通過資源類型和名稱為ILinkPolicy實例提供查找。ILinksService是框架中的主要入口點,將該接口注入用戶代碼,以將鏈接應用於API資源。ILinkTransformationContextFactory控制著在轉換過程中如何創建鏈接的轉換上下文。IRouteMap ,控制如何索引您的API以允許路由之間的鏈接。從1.0.x版本到1.1.x版本的更改大多是非破壞的,但是,如果您實現了任何自定義需求處理程序,如上面的示例所述的任何自定義要求處理程序,則base類LinksHandler的簽名略有更改以刪除通用類型TResource的重複聲明。
在v1.0.x中,您的代碼可能看起來像:
public class MyCustomHandler : ILinksHandler { .. . }現在,它應該從LinksHandler<TRequirement>繼承,使實現更簡單,並給出類型安全的HandleRequirementAsync替代,使您可以訪問正確的要求。
public class MyCustomHandler : LinksHandler < MyCustomRequirement >