ASP.NET Core Web API - 在创建的实体的链接中包含 API 版本

问题描述 投票:0回答:2

我正在我的 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。我该怎么办?

c# .net asp.net-core versioning webapi
2个回答
0
投票

只需添加

[Area("api")]

至控制器


0
投票

这应该有效。不过我有几个问题。您使用哪个版本的库?这个问题已经解决很多年了。

_linkGenerator
从哪里来?是直接还是间接从DI解析出来的?从提供的信息来看并不清楚。

说明

当您按 URL 段进行版本控制时,您必须使用

ApiVersionRouteContraint
(您就是)。路由参数有两部分:名称和可选约束。
{version:apiVersion}
具有名称
version
和约束键
apiVersion
。您可以使用任何您想要的名称作为name。约束键必须与已注册的内容匹配。
ApiVersioningOptions.RouteConstraintName
默认为
apiVersion
,但您可以更改它。

约束匹配过程中会发生一些简单的事情:

  1. value
    被捕获为
    IApiVersioningFeature.RawRequestedApiVersion
  2. value
    被解析并打开:
    1. 成功设置为
      IApiVersioningFeature.RequestedApiVersion
    2. 失败,不匹配(例如,最终会导致
      404
  3. key
    被捕获为
    IApiVersioningFeature.RouteParameter

最后一部分是关键。

默认

LinkGenerator
不知道这个参数。假设您使用名称
version
,链接生成器不知道如何填充它。您的操作中可能没有
version
参数。更重要的是,
ApiVersion
的模型绑定器是
BindingSource.Special
,因为它可以来自不同的甚至多个位置。
LinkGenerator
具有后备行为,可将其不知道如何处理的所有内容放在查询字符串上(这就是您所看到的行为)。

当您调用

AddApiVersioning
时,它将注册或装饰
LinkGenerator
的现有注册,这是 API 版本感知的。只要
IApiVersioningFeature.RouteParameter
IApiVersioningFeature.RawRequestedApiVersion
非空并且不存在于
RouteValueDictionary
中,它们就会被添加。

有一些注意事项:

  1. 该值不会被设计覆盖。如果您明确将
    version
    设置为某个值,则不会对其进行处理。
  2. 如果目标 URL 不使用名称
    version
    ,则链接生成可能会错误。
  3. 如果您采用混合风格的版本控制,并且您来自不按 URL 进行版本控制的地方,并且您尝试为采用 URL 进行版本控制的端点构建链接,则无需使用
    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
显式装饰实例。

© www.soinside.com 2019 - 2024. All rights reserved.