Swagger not working correctly with multiple versions of ASP.NET WebApi app
请帮帮我,一开始看起来很容易,现在我在项目中迟到了:
我正在尝试为 ASP.NET WebApi 项目以及 Swagger 设置 API 版本控制。 API 版本控制工作正常,调用不同的版本会返回正确的结果(见下文)。
相反,Swagger 无法同时提供这两个版本。在调试时,我注意到当在 SwaggerConfig.cs 中调用
有人可以指出需要做些什么来解决这个问题并让 Swagger 也适用于这两个版本吗?
下面是 API 版本控制的代码和证明,而 Swagger 仅适用于 v1.0。
谢谢!
调用 API v1.0 有效:
调用 API v1.1 也可以:
Swagger for v1.0 很好:
(http://localhost:50884/v1.0/swagger)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | { "swagger":"2.0", "info":{ "version":"v1.0", "title":"My API v1.0" }, "host":"localhost:50884", "schemes":[ "http" ], "paths":{ "/api/ping":{ "get":{ "tags":[ "Ping" ], "summary":"Get a pong.", "operationId":"GetAPong", "consumes":[ ], "produces":[ "application/json", "text/json", "application/xml", "text/xml" ], "responses":{ "200":{ "description":"OK" }, "404":{ "description":"NotFound" } } } } }, "definitions":{ } } |
v1.1 的 Swagger 为空:
(http://localhost:50884/v1.1/swagger)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { "swagger":"2.0", "info":{ "version":"v1.1", "title":"My API v1.1" }, "host":"localhost:50884", "schemes":[ "http" ], "paths":{ }, "definitions":{ } } |
代码
App_Start\\\\\\\\WebApiConfig.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.AddApiVersioning(options => { options.ReportApiVersions = true; }); var constraintResolver = new System.Web.Http.Routing.DefaultInlineConstraintResolver(); constraintResolver.ConstraintMap.Add("apiVersion", typeof(Microsoft.Web.Http.Routing.ApiVersionRouteConstraint)); config.MapHttpAttributeRoutes(constraintResolver); config.Routes.MapHttpRoute( name:"DefaultApi", routeTemplate:"api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } |
App_Start\\\\\\\\SwaggerConfig.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class SwaggerConfig { static string XmlCommentsFilePath { get { var basePath = System.AppDomain.CurrentDomain.RelativeSearchPath; var fileName = typeof(SwaggerConfig).GetTypeInfo().Assembly.GetName().Name +".xml"; return Path.Combine(basePath, fileName); } } public static void Register() { var configuration = GlobalConfiguration.Configuration; GlobalConfiguration.Configuration.EnableSwagger("{apiVersion}/swagger", c => { c.OperationFilter<SwaggerDefaultValues>(); c.MultipleApiVersions((System.Web.Http.Description.ApiDescription apiDesc, string targetApiVersion) => { var attr = apiDesc.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<Microsoft.Web.Http.ApiVersionAttribute>().FirstOrDefault(); if (attr == null && (targetApiVersion =="v1" || targetApiVersion =="v1.0")) return true; var match = (attr != null) && (attr.Versions.FirstOrDefault(v =>"v" + v.ToString() == targetApiVersion) != null); return match; }, (vc) => { vc.Version("v1.1","My API v1.1"); vc.Version("v1.0","My API v1.0"); }); c.IncludeXmlComments(SwaggerConfig.XmlCommentsFilePath); }) .EnableSwaggerUi(c => { c.DocExpansion(DocExpansion.List); c.EnableDiscoveryUrlSelector(); }); } } |
v1.0 和 v1.1 的控制器(位于同一命名空间中)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | [ApiVersion("1.0")] [RoutePrefix("api")] [ControllerName("Ping")] public class PingController : ApiController { [HttpGet] [Route("ping")] [SwaggerOperation("GetAPong")] [SwaggerResponse(HttpStatusCode.OK)] [SwaggerResponse(HttpStatusCode.NotFound)] public string Get() { return"Pong v1.0"; } } [ApiVersion("1.1")] [RoutePrefix("api")] [ControllerName("Ping")] public class Ping11Controller : ApiController { [HttpGet] [Route("ping")] [SwaggerOperation("GetAPong")] [SwaggerResponse(HttpStatusCode.OK)] [SwaggerResponse(HttpStatusCode.NotFound)] public string Get() { return"Pong v1.1"; } } |
包裹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?xml version="1.0" encoding="utf-8"?> <packages> <package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net46" /> <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net46" /> <package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net46" /> <package id="Microsoft.AspNet.WebApi.Versioning" version="2.1.0" targetFramework="net46" /> <package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net46" /> <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.7" targetFramework="net46" /> <package id="Microsoft.IdentityModel.Logging" version="1.1.4" targetFramework="net46" /> <package id="Microsoft.IdentityModel.Tokens" version="5.1.4" targetFramework="net46" /> <package id="Microsoft.Net.Compilers" version="2.3.2" targetFramework="net46" developmentDependency="true" /> <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net46" /> <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net46" /> <package id="NLog" version="4.4.12" targetFramework="net46" /> <package id="Swashbuckle" version="5.6.0" targetFramework="net46" /> <package id="Swashbuckle.Core" version="5.6.0" targetFramework="net46" /> <package id="System.IdentityModel.Tokens.Jwt" version="5.1.4" targetFramework="net46" /> <package id="WebActivatorEx" version="2.2.0" targetFramework="net46" /> </packages> |
已解决:
使用如下版本的 API 资源管理器(请注意,由于初始化问题,我不得不将代码从 SwaggerConfig.cs 移至 WebApiConfig.cs):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | var apiExplorer = config.AddVersionedApiExplorer(options => { options.GroupNameFormat ="'v'VVV"; }); var versionSupportResolver = new Func<ApiDescription, string, bool>((apiDescription, version) => apiDescription.GetGroupName() == version); var versionInfoBuilder = new Action<VersionInfoBuilder>(info => { foreach (var group in apiExplorer.ApiDescriptions) { info.Version(group.Name, $"MyAPI v{group.ApiVersion}"); } }); config .EnableSwagger("{apiVersion}/swagger", swagger => { swagger.OperationFilter<SwaggerDefaultValues>(); swagger.MultipleApiVersions(versionSupportResolver, versionInfoBuilder); swagger.IncludeXmlComments(WebApiConfig.XmlCommentsFilePath); }) .EnableSwaggerUi(swaggerUi => { swaggerUi.EnableDiscoveryUrlSelector(); swaggerUi.DocExpansion(DocExpansion.List); }); |
ValueV1Controller.cs
1 2 3 4 5 6 7 8 9 | [RoutePrefix("api/v1/value")] public class ValueV1Controller : ApiController { [Route("get")] public IEnumerable<string> Get() { return new string[] {"value1","value2" }; } } |
ValueV2Controller.cs
1 2 3 4 5 6 7 8 9 | [RoutePrefix("api/v2/value")] public class ValueV2Controller : ApiController { [Route("get")] public IEnumerable<string> Get() { return new string[] {"value1.2","value2.2" }; } } |
SwaggerConfig.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public class SwaggerConfig { public static void Register() { var thisAssembly = typeof(SwaggerConfig).Assembly; GlobalConfiguration.Configuration .EnableSwagger(c => { c.MultipleApiVersions( (apiDesc, version) => { var path = apiDesc.RelativePath.Split('/'); var pathVersion = path[1]; return CultureInfo.InvariantCulture.CompareInfo.IndexOf(pathVersion, version, CompareOptions.IgnoreCase) >= 0; }, (vc) => { vc.Version("v2","Swashbuckle Dummy API V2"); vc.Version("v1","Swashbuckle Dummy API V1"); }); }) .EnableSwaggerUi(c => { c.EnableDiscoveryUrlSelector(); }); } } |
我遇到了同样的错误,但为我解决的是将 EnableSwagger 扩展方法中的 templateRoute 从 swagger/docs/{apiVersion} 更改为 swagger/{apiVersion}/docs。
我认为当 api 版本位于路由末尾时,次要版本存在解析问题(可能是因为它包含 \\'.\\')。