【问题标题】:API Versioning in .NET Core - AssumeDefaultVersionWhenUnspecified.NET Core 中的 API 版本控制 - AssumeDefaultVersionWhenUnspecified
【发布时间】:2021-09-11 10:38:39
【问题描述】:

我正在考虑为我们现有的 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 资源 'http://{{host}}/api/customers/{customer}/estates/{estate}/meters/{meter }-{installation}/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 但这似乎不起作用。

【问题讨论】:

    标签: asp.net-core aspnet-api-versioning


    【解决方案1】:

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

    “有没有办法告诉 Microsoft.AspNetCore.Mvc.Versioning 一个特定的方法应该可用,而不管版本如何?”

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

    您的设置并不完整。您似乎正在向现有的一组 API 添加 API 版本控制。 DefaultApiVersion 的主要目的之一是在没有其他信息可用时设置默认 API 版本。如果您没有应用任何 API 版本 - 例如,使用属性,那么这将是所有 API 的隐式 API 版本。这使您不必改造所有现有的控制器和代码。不太明显的是,一旦您应用 any 显式 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 版本映射,因为您很可能会在某些时候破坏客户端。如果您仅使用 double-routes 来向后兼容原始 API 版本,那么您应该没问题。当您最终为未来的 API 版本创建新控制器时,模板中只有一个带有 API 版本的路由。

    【讨论】:

    • 嗨,克里斯,感谢您的广泛回答,这让我开始思考一般的方法。我们选择只向消费者公开新的主要版本,这有助于“提升”未更改的控制器和端点的过程。我尝试使用 [ApiVersionNeutral] 进行一些原型设计,但我清楚地看到,虽然它一开始就可以工作,但“如果”以后需要某个版本,就会遇到路由命名问题。
    • 很高兴有帮助。 版本中立在适当的情况下可能很有用,但如果你把东西扔到墙上作为简单的catch all,它可能会给你带来麻烦。可以帮助您的另一件事是制定明确的政策 - 例如N-2 版本。这可以显着减少您必须维护的版本数量。
    • 您好,Chris,是的,我们已经与客户达成协议,在没有明确终止旧版本的日期之前,永远不会推出新版本。幸运的是,我们知道我们 API 的消费者,因此可以“决定”日落日期。澄清一下,我们已选择包含仍在草稿中的 Sunset 标头,以明确说明一旦某些事情将要消失。
    猜你喜欢
    • 2016-12-17
    • 2019-01-13
    • 1970-01-01
    • 2019-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多