我正在我的 WebAPI 项目中实施 HATEOAS 原则。将任何给定类型的新实体添加到数据库后,响应不仅应包含新创建的实体,还应包含指向相应控制器中的 GET、PUT 和 DELETE 端点的 3 个链接。另外,我在端点 URI 中使用 API 版本控制。
所以,我的控制器是用属性装饰的:
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("3.0")]
Startup.cs 中的 API 版本控制配置
services.AddApiVersioning(opt =>
{
opt.AssumeDefaultVersionWhenUnspecified = true;
opt.DefaultApiVersion = ApiVersion.Default;
opt.ReportApiVersions = true;
});
为实体生成链接的方法:
public override List<ResourceUri> GetResourceUris(int id, ApiVersion apiVersion)
{
return new List<ResourceUri>
{
new ResourceUri
{
Method = "GET",
Uri = this._linkGenerator.GetUriByAction(
HttpContext,
nameof(GetResourceById),
values: new
{
Id = id,
area = "api",
version = apiVersion.ToString()
})
},
new ResourceUri
{
Method = "PUT",
Uri = this._linkGenerator.GetUriByAction(HttpContext, nameof(UpdateItem), values: new { id })
},
new ResourceUri
{
Method = "DELETE",
Uri = this._linkGenerator.GetUriByAction(HttpContext, nameof(DeleteItem), values: new { id })
},
};
}
部分
values: new
{
Id = id,
area = "api",
version = apiVersion.ToString()
})
取自here,它被标记为已接受的答案。
但是,每次我发布新实体时,结果都是这样的:
{
"uri": "https://localhost:44386/api/v1/Resources/43?area=api&version=3",
"method": "GET"
}
区域和版本值仅作为查询参数包含,API 版本始终设置为“v1”而不是“v3”。
给予
AssumeDefaultVersionWhenUnspecified
设置为 True,我使用错误的方式向 LinkGenerator 提供 ApiVersion。我该怎么办?
只需添加
[Area("api")]
至控制器
这应该有效。不过我有几个问题。您使用哪个版本的库?这个问题已经解决很多年了。
_linkGenerator
从哪里来?是直接还是间接从DI解析出来的?从提供的信息来看并不清楚。
当您按 URL 段进行版本控制时,您必须使用
ApiVersionRouteContraint
(您就是)。路由参数有两部分:名称和可选约束。 {version:apiVersion}
具有名称 version
和约束键 apiVersion
。您可以使用任何您想要的名称作为name。约束键必须与已注册的内容匹配。 ApiVersioningOptions.RouteConstraintName
默认为 apiVersion
,但您可以更改它。
约束匹配过程中会发生一些简单的事情:
value
被捕获为 IApiVersioningFeature.RawRequestedApiVersion
value
被解析并打开:
IApiVersioningFeature.RequestedApiVersion
404
)key
被捕获为 IApiVersioningFeature.RouteParameter
最后一部分是关键。
默认
LinkGenerator
不知道这个参数。假设您使用名称 version
,链接生成器不知道如何填充它。您的操作中可能没有 version
参数。更重要的是,ApiVersion
的模型绑定器是 BindingSource.Special
,因为它可以来自不同的甚至多个位置。 LinkGenerator
具有后备行为,可将其不知道如何处理的所有内容放在查询字符串上(这就是您所看到的行为)。
当您调用
AddApiVersioning
时,它将注册或装饰 LinkGenerator
的现有注册,这是 API 版本感知的。只要IApiVersioningFeature.RouteParameter
和IApiVersioningFeature.RawRequestedApiVersion
非空并且不存在于RouteValueDictionary
中,它们就会被添加。
有一些注意事项:
version
设置为某个值,则不会对其进行处理。version
,则链接生成可能会错误。version
参数。考虑以下控制器:
[ApiVersion( 1.0 )]
[Route( "api/v{version:apiVersion}/[controller]" )]
public class HelloWorldController : ControllerBase
{
[HttpGet( "{id:int}" )]
public IActionResult Get( int id, ApiVersion apiVersion ) =>
Ok( new { Controller = GetType().Name, Id = id } );
[HttpPost]
public IActionResult Post( ApiVersion apiVersion ) =>
CreatedAtAction( nameof( Get ), new { id = 42 }, null );
}
如果您发送请求:
POST /api/v1/helloworld/42 HTTP/2
Host: example.api.com
您将收到回复:
HTTP/2 201 Created
Location: https://example.api.com/api/v1/helloworld/42
它使用
LinkGenerator
构建 Location
标头字段值。
错误的 URL 具有
v1
而不是没有值,这一事实使我相信路由模板可能具有硬编码值而不是所需的路由约束(例如 api/v1/Resources/{id}
与 api/v{version:apiVersion}/Resources/{id}
)。这肯定会阻止它按预期工作。
如果
LinkGenerator
是在 DI 之外构造的,那么它不支持 API 版本,也不会按预期运行。如果您需要此功能,那么您应该使用 ApiVersionLinkGenerator
显式装饰实例。