API .NET Core 中的版本控制 - AssumeDefaultVersionWhenUnspecified

API Versioning in .NET Core - AssumeDefaultVersionWhenUnspecified

我正在考虑将版本控制添加到现有的 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}-{installation}/consumption/detail' 匹配的 HTTP 资源

只要我添加 [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 但是好像不行。

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

"Is there any way to tell Microsoft.AspNetCore.Mvc.Versioning that a particular method should be available regardless of versions?"

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

没有您的设置的完整图片。看来您要将 API 版本控制添加到现有的 API 集。 DefaultApiVersion 的主要目的之一是在没有其他信息可用时设置默认 API 版本。如果您没有应用任何 API 版本 - 例如,使用属性,那么这将是所有 API 的隐式 API 版本。这使您不必改造所有现有的控制器和代码。不太明显的是,一旦应用 any 显式 API 版本,隐式规则将被忽略。这确保隐式版本可以被淘汰。默认版本通常无关紧要。它只是一种让您表明初始 API 版本最初是什么的方式。当 grandfathering in existing APIs.

时最相关

鉴于您的配置:

// 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
{
}

Grandfathered controller with implicit versioning

// 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
{
}

Controller with explicit versioning

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

您似乎在描述 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();
}

Controller with version interleaving

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

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

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