Swagger无法正常使用多个版本的ASP.NET WebApi应用程序

时间:2017-09-05 15:46:48

标签: c# asp.net-web-api swagger swashbuckle

请帮助我,一开始看起来很容易,现在我在项目的后期:

我正在尝试为Swager和ASP.NET WebApi项目设置API版本控制。 API版本控制正常,调用不同的版本会返回正确的结果(见下文)。

相反,Swagger无法为这两个版本提供服务。在调试时,我注意到在SwaggerConfig.cs中调用c.MultipleApiVersions(...)时,apiDesc.ActionDescriptor.ControllerDescriptor报告的控制器始终为PingController而永远不会Ping11Controller

有人可以指出要解决这个问题需要做些什么,并且Swagger也适用于这两个版本吗?

下面,在Swagger仅适用于v1.0的情况下,API版本控制的代码和证明正常工作。

谢谢!

调用API v1.0有效: enter image description here

调用API v1.1也有效: enter image description here

Swagger for v1.0很好:http://localhost:50884/v1.0/swagger

{
   "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":{
   }
}

Swagger for v1.1为空:http://localhost:50884/v1.1/swagger

{
   "swagger":"2.0",
   "info":{
      "version":"v1.1",
      "title":"My API v1.1"
   },
   "host":"localhost:50884",
   "schemes":[
      "http"
   ],
   "paths":{
   },
   "definitions":{
   }
}

代码

App_Start \ WebApiConfig.cs:

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:

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的控制器(位于相同的命名空间中)

[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";
    }
}

PACKAGES

<?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>

3 个答案:

答案 0 :(得分:1)

通过以下方式解决:

  1. 添加 Microsoft.AspNet.WebApi.Versioning.ApiExplorer
  2. 使用如下版本化的API资源管理器(请注意,由于初始化问题,我必须从WebApiConfig.cs中的SwaggerConfig.cs移动代码):

        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);
            });
    

答案 1 :(得分:1)

enter image description here

ValueV1Controller.cs

[RoutePrefix("api/v1/value")]
public class ValueV1Controller : ApiController
{
    [Route("get")]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

ValueV2Controller.cs

[RoutePrefix("api/v2/value")]
public class ValueV2Controller : ApiController
{
    [Route("get")]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1.2", "value2.2" };
    }
}

SwaggerConfig.cs

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();
            });
    }
}

答案 2 :(得分:0)

我遇到了同样的错误,但为我解决的是将 EnableSwagger 扩展方法中的 templateRoute 从 swagger/docs/{apiVersion} 更改为 招摇/{apiVersion}/docs。 我认为当 api 版本在路由的末尾时,次要版本存在解析问题(可能是因为它包含一个“.”)。