ASPNETコアWeb APIプロジェクトのHateOASの実装により、APIから返されるモデルに適用するリンクを完全に制御できます。さまざまな状態をエンドユーザーに伝えるために、このライブラリは承認と完全に統合され、任意の条件がAPIリソース間のハトオアスリンクを表示または非表示にするかどうかを判断することができます。
nuget.orgからパッケージをインストールします
PM > Install-Package RiskFirst.Hateoasこれには、バージョン3.0.0で導入された依存関係Riskfirst.hateoas.modelsが含まれます。
各モデルに含めるリンクを構成します。
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を使用してコントローラーメソッドをマークアップして、適用されるデフォルトのポリシーをオーバーライドすることもできます。以下のコードは、 AddLinksAsyncへの呼び出しのポリシー名を指定することなく、「FullInfo」プロファイルを返されたモデルに適用します。
[ 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 ;
}
} ILinksPolicyのインスタンスまたは実行時に評価されるILinksRequirementの配列を取得するAddLinksAsyncの過剰量がさらにあります。これにより、APIコード内の任意の時点でどのリンクが適用されるかを完全に制御する必要があります。
Href変換方法を変更する必要はあまりないはずですが、1つの一般的な要件は、絶対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 } " ) ;
} ) ;変換をカスタマイズする両方の方法は、LinkConfigureationSampleで見ることができます。
各モデルに含まれるリンクを制御することを希望する可能性があり、1つの一般的な要件は、現在のユーザーが承認されているリンクのみを表示することです。このライブラリは、認証パイプラインに完全に統合され、リンクされたアクションに適用した承認ポリシーを適用します。
リンクで承認を有効にするには、 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プロジェクト内のAUTHRIZINITIONの詳細については、Microsoftドキュメントを参照してください。
上記の例と同様に、ポリシー名、絶対ポリシー、または一連の要件を指定できるさらなる条件方法があります。
また、 Assert条件を使用して、ブールのロジックに基づいてリンクを条件付きで表示することもできます。たとえば、オブジェクトのページング結果に一般的なページングリンクを追加できる方法があります。合計で1ページの結果がある場合、これらは価値がないと判断する場合があります。
options . AddPolicy < IPageLinkContainer > ( policy =>
{
policy . RequireelfLink ( "all" )
. RequirePagingLinks ( condition => condition . Assert ( x => x . PageCount > 1 ) ) ;
} ) ;LinksPolicyBuilderのgeneric 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 、リンクのRel&HREFの適切な変換中に変換コンテキストがどのように作成されるかを制御します。IRouteMap 、APIのインデックス作成方法を制御して、ルート間のリンクを許可します。バージョン1.0.xから1.1.xへの変更はほとんど破損していませんでしたが、基本クラスの署名の例で説明されているようにカスタム要件ハンドラーを実装した場合、リンクLinksHandlerドラーの署名はわずかに変更され、一般的なタイプのTResourceの重複宣言を削除します。
v1.0.xでは、コードは次のように見えたかもしれません。
public class MyCustomHandler : ILinksHandler { .. . } LinksHandler<TRequirement>実装をより簡単に継承し、 HandleRequirementAsyncのタイプセーフオーバーライドを提供して、正しくタイプの要件にアクセスできるようにする必要があります。
public class MyCustomHandler : LinksHandler < MyCustomRequirement >