自動給 Asp.Net Core WebApi 增加 ApiVersionNeutral

自動給 Asp.Net Core WebApi 增加 ApiVersionNeutral

Intro

添加加一個 Controller 的時候,經常忘記在 Controller 上增加 ApiVersion ,結果就導致前端使用指定的 ApiVersion 訪問的時候就會失敗,不支持的 Api 版本。

錯誤信息如下:

{
    "error": {
        "code": "UnsupportedApiVersion",
        "message": "The HTTP resource that matches the request URI 'http://localhost:5000/api/values' does not support the API version '1.2'.",
        "innerError": null
    }
}

分析源代碼

Asp.Net Core ApiVersion 源碼地址:https://github.com/Microsoft/aspnet-api-versioning

使用 ApiVersion 會在註冊服務的地方註冊 ApiVersion 相關的服務

    services.AddApiVersioning();

找到源碼 會發現註冊服務的時候把 mvc 默認的 ActionSelector 替換成了 ApiVersionActionSelector,然後查看 ApiVersionActionSelector 的源碼,找到了以下幾處關鍵代碼

ApiVersion 服務註冊

IServiceCollectionExtensions

ApiVersionNetural

apiversionneutral

ApiVersionNeutralAttribute

ApiVersionNeutralAttribute

ApiVersionActionSelector

ApiVersionActionSelector

ControllerApiVentionBuilder

ControllerApiVentionBuilder

總結如下:

如果 Controller 的 Attribute 定義的有 ApiVersionNeutralAttribute 就會忽略 ApiVersion 的限制,即使沒有使用 ApiVersion 或者使用任意一個 ApiVersion 都可以路由到 Action,都可以訪問得到,也不會出現開篇提到的錯誤。

解決方案

可以自己實現一個 IControllerModelConvention,去給沒有定義 ApiVersion 的控制器加 ApiVersionNeutralAttribute,實現代碼如下:

public class ApiControllerVersionConvention : IControllerModelConvention
{
    public void Apply(ControllerModel controller)
    {
        if (!(controller.ControllerType.IsDefined(typeof(ApiVersionAttribute)) || controller.ControllerType.IsDefined(typeof(ApiVersionNeutralAttribute))))
        {
            if (controller.Attributes is List<object>
                attributes)
            {
                attributes.Add(new ApiVersionNeutralAttribute());
            }
        }
    }
}

在註冊 Mvc 服務的時候,配置 MvcOptions

services.AddMvc(options =>
    {
        options.Conventions.Add(new ApiControllerVersionConvention());
    });

啟動項目,這時候再訪問原來因為沒有定義 ApiVersion 的控制器下的路由,這時就不會再報錯了,使用任意一個 ApiVersion 也都不會有問題了,問題解決啦~~~

擴展方法

為了方便使用,你也可以加一個擴展方法,在擴展方法裏配置 MvcOptions,根據自己的需要,我覺得兩種方式都 OK 的,擴展方法示例如下:

public static class MvcBuilderExtensions
{
    public static IMvcBuilder AddApiControllerVersion(this IMvcBuilder builder)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }
        builder.Services.Configure<MvcOptions>(options=> options.Conventions.Add(new ApiControllerVersionConvention()));
        return builder;
    }
}

使用的時候可以直接在 AddMvc 之後加上擴展方法就可以了

services.AddMvc()
    .AddApiControllerVersion();

End

問題解決,完美收官,最後還是要説一下,注意這個的使用情景,如果你要指定一個默認的 ApiVersion 有更好的方法,直接配置 ApiVersioningOptions 中的 DefaultApiVersion
就可以了

services.AddApiVersioning(options =>
    {
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.DefaultApiVersion = ApiVersion.Default;
    });

如果你的 ApiVersion 不定,可能有些 Api 的 ApiVersion 會經常變,可以使用這種方式。

有問題歡迎聯繫~~

關鍵詞:apiversion 使用 可以 controller options 方法 core net api 擴展

相關推薦:

帶着新人學springboot的應用08(springboot+jpa的集成)

<深入理解Abp> 進程啟動 - 一切的開始

打造自己的.NET Core項目模板

Asp.Net Core 輕鬆學-利用 Swagger 自動生成接口文檔

COMCMS v0.9 版本發佈,帶前後端的一個響應式企業站

【Vue】IView之table組件化學習(二)

[Abp 源碼分析]十七、ASP.NET Core 集成

API Versioning in .NET Core

.Net Core中的Api版本控制

Moving Canary deployments on AWS using ELB to kubernetes using Traefik