自動給 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 服務注冊
ApiVersionNetural
ApiVersionNeutralAttribute
ApiVersionActionSelector
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 會經常變,可以使用這種方式。
有問題歡迎聯系~~