0

我正在考虑将版本控制添加到我们拥有的现有 API 中。我们在 URL 中嵌入版本。

版本控制的要求是添加新版本应该很容易,但不是所有内容都会改变,我们不想在获得新版本时遍历所有控制器并添加新版本属性。有没有办法告诉 Microsoft.AspNetCore.Mvc.Versioning 无论版本如何,特定方法都应该可用?

我尝试使用 version:apiVersion 和仅使用基本路线来装饰我的方法。

[Route("v{version:apiVersion}/customers/{customerId}/estates/{estateId:int}/meters/{meterNumber}-{installationId}/consumption/detail")
[Route("customers/{customerId}/estates/{estateId:int}/meters/{meterNumber}-{installationId}/consumption/detail")]

我的配置是这样的(版本号 3 仅用于测试目的):

services.AddApiVersioning(config =>
        {
            config.DefaultApiVersion = new ApiVersion(3, 0);
            config.AssumeDefaultVersionWhenUnspecified = true;
            config.ReportApiVersions = true;
        });

使用此配置调用端点时,我得到“与请求 URI 'http://{{host}}/api/customers/{customer}/estates/{estate}/meters/{meter}-{不支持安装}/consumption/detail'

一旦我添加了 [ApiVersion("3.0")] 属性,一切正常。但我的想法是,如果我目前在版本 2 上运行,但对 API 其他部分的更改需要新版本和 API 的默认版本,我不想去这个控制器并“碰撞”版本. 除非我指定具体内容,否则它应该继续响应。

如果我将 AssumeDefaultVersionWhenUnspecified 更改为 false,我会收到“需要 API 版本,但未指定”。但我预计它只会抓住没有版本的路线?

我已经阅读了这里的限制:https ://github.com/Microsoft/aspnet-api-versioning/wiki/Known-Limitations#url-path-segment-routing-with-a-default-api-version 但它没有似乎不起作用。

4

1 回答 1

2

设置AssumeDefaultVersionWhenUnspecified = true是一个被高度滥用的功能。这只是为了促进向后兼容性。一旦你选择了 API 版本控制,所有的 API 控制器都有一些隐式或显式的 API 版本。假设版本提供了一种机制来处理“原始”、未命名的版本,否则会破坏不知道在请求中包含 API 版本的现有客户端。

“有什么方法可以告诉 Microsoft.AspNetCore.Mvc.Versioning,无论版本如何,都应该可以使用特定的方法?”

是的,但我不完全确定这就是你要找的。API 可以是版本中立的,这意味着它将接受任何和所有 API 版本,包括根本不接受。这对于某些类型的 API(例如/ping运行状况检查或)很有用DELETE,它们通常不会随时间而改变。这样的 API 可以用[ApiVersionNeutral]. 这可以针对控制器上的所有 API 或特定操作。请注意,一旦您选择了这条路径,“只能有一个”。

没有您的设置的完整图片。您似乎正在向现有的一组 API 添加 API 版本控制。的主要目的之一DefaultApiVersion是在没有其他信息可用时设置默认 API 版本。如果您没有应用任何 API 版本 - 例如,使用属性,那么这将是所有 API 的隐式 API 版本。这使您不必改造所有现有的控制器和代码。不太明显的是,一旦应用任何显式 API 版本,隐式规则就会被忽略。这确保了隐式版本可以日落。默认版本通常无关紧要。这只是一种指示最初的 API 版本是什么的方式。祖父时最相关在现有的 API 中。

鉴于您的配置:

// implicitly 3.0 because DefaultApiVersion = 3.0 and there
// are no explicit versions from attributes or conventions
[Route("customers/{customerId}/estates/{estateId:int}/meters/{meterNumber}-{installationId}/consumption/detail")]
public class CustomerController : ControllerBase
{
}

具有隐式版本控制的祖父控制器

// explicitly 2.0 due to attributes; DefaultApiVersion is ignored
[ApiVersion("2.0")]
[Route("v{version:apiVersion}/customers/{customerId}/estates/{estateId:int}/meters/{meterNumber}-{installationId}/consumption/detail")
[Route("customers/{customerId}/estates/{estateId:int}/meters/{meterNumber}-{installationId}/consumption/detail")]
public class CustomerController : ControllerBase
{
}

具有显式版本控制的控制器

除了支持向后兼容的原始 API 版本外,所有 API 版本都是显式和离散的;这是设计使然。模糊匹配不会以可预测的方式工作,因此精确匹配很重要。

您似乎在描述 API 版本交错 - 例如在单个控制器实现上实现的多个 API 版本。我想它可能看起来像:

[ApiVersion("2.0")]
[ApiVersion("3.0")]
[Route("v{version:apiVersion}/customers/{customerId}/estates/{estateId:int}/meters/{meterNumber}-{installationId}/consumption/detail")
[Route("customers/{customerId}/estates/{estateId:int}/meters/{meterNumber}-{installationId}/consumption/detail")]
public class CustomerController : ControllerBase
{
    // implicitly maps to 2.0 from the controller, but does not
    // match 3.0 because there is an explicit 3.0 mapping
    [HttpGet]
    public IActionResult Get(
        string customerId,
        int estateId,
        string meterNumber,
        string installationId ) => Ok();

    // explicitly maps to 3.0 only
    [MapToApiVersion("3.0")]
    [HttpGet]
    public IActionResult GetV3(
        string customerId,
        int estateId,
        string meterNumber,
        string installationId ) => Ok();

    // explicitly maps to 3.0 only
    // a 2.0 request produces 405
    [MapToApiVersion("3.0")]
    [HttpPut]
    public IActionResult Put(
        string customerId,
        int estateId,
        string meterNumber,
        string installationId
        [FromBody] ComsumptionDetail detail ) => Ok();
}

具有版本交错的控制器

DefaultApiVersion只能应用单个隐式 API 版本。这意味着永远不会发生与它的交错。一旦您开始显式添加版本,您还必须开始包含隐式版本,因为没有其他方法可以指示不应再匹配隐式版本。

在路由定义和 API 版本映射方面,有几种方法可以用来最小化代码流失。VersionByNamespaceConvention是您可以应用的内置约定,它将从控制器类型的 .NET 命名空间派生 API 版本。这可以使添加、删除和组织控制器及其 API 版本变得非常容易。他们可以使用交错,因为映射是相加的,但这可能会让人难以维护。您也可以定义自己的约定。

双路由注册是 URL 段版本控制的结果。它是最少 RESTful 的版本控制方法,因为它违反了统一接口约束。没有其他版本控制方法存在此问题。我建议不要浮动默认路由和 API 版本映射,因为您很可能会在某些时候破坏客户端。如果您只使用双路由来向后兼容原始 API 版本,那么您应该没问题。当您最终为未来的 API 版本创建新控制器时,模板中只有一个带有 API 版本的路由。

于 2021-06-29T17:40:14.677 回答