API에서 반환 된 모델에 적용 할 링크를 완전히 제어 할 수있는 ASPNET Core Web API 프로젝트를위한 증오 도아 구현. 다양한 상태를 최종 사용자에게 전달하기 위해이 라이브러리는 승인과 완전히 통합되며 임의의 조건은 API 리소스 간의 증오 링크를 보여줄 것인지 여부를 결정할 수 있습니다.
nuget.org에서 패키지를 설치하십시오
PM > Install-Package RiskFirst.Hateoas여기에는 LinkContainer 기본 클래스를 참조하는 어셈블리에서 ASPNETCORE 의존성을 제거하기 위해 버전 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 변환되는 방식을 변경할 필요가 많지 않아야하지만, 하나의 일반적인 요구 사항은 절대 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 } " ) ;
} ) ;Transformation의 커스터마이징 방식은 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 조건을 사용하여 부울 논리를 기반으로 링크를 조건부로 표시 할 수도 있습니다. 예를 들어, 객체의 페인트 결과에 공통 페이징 링크를 추가 할 수있는 방법이 있습니다. 총 1 페이지의 결과 만 있으면 가치가 없다고 결정할 수 있습니다.
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 링크의 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 >