Swagger 无法与多个版本的 ASP.NET WebApi 应用程序一起正常工作

Swagger not working correctly with multiple versions of ASP.NET WebApi app

请帮帮我,一开始看起来很容易,现在我在项目中迟到了:

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

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

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

下面是 API 版本控制的代码和证明,而 Swagger 仅适用于 v1.0。

谢谢!

调用 API v1.0 有效:

关于 c#:Swagger 无法与多个版本的 ASP.NET WebApi 应用程序一起正常工作

调用 API v1.1 也可以:

关于 c#:Swagger 无法与多个版本的 ASP.NET WebApi 应用程序一起正常工作

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":"2.0",

 "info":{

  "version":"v1.1",

  "title":"My API v1.1"

 },

 "host":"localhost:50884",

 "schemes":[

  "http"

 ],

 "paths":{

 },

 "definitions":{

 }

}

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 }

    );

  }

}

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

      });

  }

}

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

  }

}

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

    });

[RoutePrefix("api/v1/value")]

public class ValueV1Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1","value2" };

  }

}

[RoutePrefix("api/v2/value")]

public class ValueV2Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1.2","value2.2" };

  }

}

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

      });

  }

}

v1.1 的 Swagger 为空:

(http://localhost:50884/v1.1/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":"2.0",

 "info":{

  "version":"v1.1",

  "title":"My API v1.1"

 },

 "host":"localhost:50884",

 "schemes":[

  "http"

 ],

 "paths":{

 },

 "definitions":{

 }

}

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 }

    );

  }

}

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

      });

  }

}

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

  }

}

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

    });

[RoutePrefix("api/v1/value")]

public class ValueV1Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1","value2" };

  }

}

[RoutePrefix("api/v2/value")]

public class ValueV2Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1.2","value2.2" };

  }

}

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

      });

  }

}

代码

App_Start\\\\\\\\WebApiConfig.cs:

{

 "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":"2.0",

 "info":{

  "version":"v1.1",

  "title":"My API v1.1"

 },

 "host":"localhost:50884",

 "schemes":[

  "http"

 ],

 "paths":{

 },

 "definitions":{

 }

}

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 }

    );

  }

}

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

      });

  }

}

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

  }

}

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

    });

[RoutePrefix("api/v1/value")]

public class ValueV1Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1","value2" };

  }

}

[RoutePrefix("api/v2/value")]

public class ValueV2Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1.2","value2.2" };

  }

}

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

      });

  }

}

App_Start\\\\\\\\SwaggerConfig.cs:

{

 "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":"2.0",

 "info":{

  "version":"v1.1",

  "title":"My API v1.1"

 },

 "host":"localhost:50884",

 "schemes":[

  "http"

 ],

 "paths":{

 },

 "definitions":{

 }

}

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 }

    );

  }

}

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

      });

  }

}

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

  }

}

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

    });

[RoutePrefix("api/v1/value")]

public class ValueV1Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1","value2" };

  }

}

[RoutePrefix("api/v2/value")]

public class ValueV2Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1.2","value2.2" };

  }

}

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

      });

  }

}

v1.0 和 v1.1 的控制器(位于同一命名空间中)

{

 "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":"2.0",

 "info":{

  "version":"v1.1",

  "title":"My API v1.1"

 },

 "host":"localhost:50884",

 "schemes":[

  "http"

 ],

 "paths":{

 },

 "definitions":{

 }

}

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 }

    );

  }

}

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

      });

  }

}

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

  }

}

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

    });

[RoutePrefix("api/v1/value")]

public class ValueV1Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1","value2" };

  }

}

[RoutePrefix("api/v2/value")]

public class ValueV2Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1.2","value2.2" };

  }

}

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

      });

  }

}

包裹

{

 "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":"2.0",

 "info":{

  "version":"v1.1",

  "title":"My API v1.1"

 },

 "host":"localhost:50884",

 "schemes":[

  "http"

 ],

 "paths":{

 },

 "definitions":{

 }

}

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 }

    );

  }

}

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

      });

  }

}

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

  }

}

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

    });

[RoutePrefix("api/v1/value")]

public class ValueV1Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1","value2" };

  }

}

[RoutePrefix("api/v2/value")]

public class ValueV2Controller : ApiController

{

  [Route("get")]

  public IEnumerable<string> Get()

  {

    return new string[] {"value1.2","value2.2" };

  }

}

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

      });

  }

}

已解决:

  • 添加 Microsoft.AspNet.WebApi.Versioning.ApiExplorer 包
  • 使用如下版本的 API 资源管理器(请注意,由于初始化问题,我不得不将代码从 SwaggerConfig.cs 移至 WebApiConfig.cs):

    {
    
     "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":"2.0",
    
     "info":{
    
      "version":"v1.1",
    
      "title":"My API v1.1"
    
     },
    
     "host":"localhost:50884",
    
     "schemes":[
    
      "http"
    
     ],
    
     "paths":{
    
     },
    
     "definitions":{
    
     }
    
    }
    
    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 }
    
        );
    
      }
    
    }
    
    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();
    
          });
    
      }
    
    }
    
    [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";
    
      }
    
    }
    
    <?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>
      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);
    
        });
    
    [RoutePrefix("api/v1/value")]
    
    public class ValueV1Controller : ApiController
    
    {
    
      [Route("get")]
    
      public IEnumerable<string> Get()
    
      {
    
        return new string[] {"value1","value2" };
    
      }
    
    }
    
    [RoutePrefix("api/v2/value")]
    
    public class ValueV2Controller : ApiController
    
    {
    
      [Route("get")]
    
      public IEnumerable<string> Get()
    
      {
    
        return new string[] {"value1.2","value2.2" };
    
      }
    
    }
    
    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();
    
          });
    
      }
    
    }

  • 关于 c#:Swagger 无法与多个版本的 ASP.NET WebApi 应用程序一起正常工作

    ValueV1Controller.cs

    {
    
     "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":"2.0",
    
     "info":{
    
      "version":"v1.1",
    
      "title":"My API v1.1"
    
     },
    
     "host":"localhost:50884",
    
     "schemes":[
    
      "http"
    
     ],
    
     "paths":{
    
     },
    
     "definitions":{
    
     }
    
    }
    
    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 }
    
        );
    
      }
    
    }
    
    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();
    
          });
    
      }
    
    }
    
    [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";
    
      }
    
    }
    
    <?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>
      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.Multip				

相关推荐

  • Spring部署设置openshift

    Springdeploymentsettingsopenshift我有一个问题让我抓狂了三天。我根据OpenShift帐户上的教程部署了spring-eap6-quickstart代码。我已配置调试选项,并且已将Eclipse工作区与OpehShift服务器同步-服务器上的一切工作正常,但在Eclipse中出现无法消除的错误。我有这个错误:cvc-complex-type.2.4.a:Invali…
    2025-04-161
  • 检查Java中正则表达式中模式的第n次出现

    CheckfornthoccurrenceofpatterninregularexpressioninJava本问题已经有最佳答案,请猛点这里访问。我想使用Java正则表达式检查输入字符串中特定模式的第n次出现。你能建议怎么做吗?这应该可以工作:MatchResultfindNthOccurance(intn,Patternp,CharSequencesrc){Matcherm=p.matcher…
    2025-04-161
  • 如何让 JTable 停留在已编辑的单元格上

    HowtohaveJTablestayingontheeditedcell如果有人编辑JTable的单元格内容并按Enter,则内容会被修改并且表格选择会移动到下一行。是否可以禁止JTable在单元格编辑后转到下一行?原因是我的程序使用ListSelectionListener在单元格选择上同步了其他一些小部件,并且我不想在编辑当前单元格后选择下一行。Enter的默认绑定是名为selectNext…
    2025-04-161
  • Weblogic 12c 部署

    Weblogic12cdeploy我正在尝试将我的应用程序从Tomcat迁移到Weblogic12.2.1.3.0。我能够毫无错误地部署应用程序,但我遇到了与持久性提供程序相关的运行时错误。这是堆栈跟踪:javax.validation.ValidationException:CalltoTraversableResolver.isReachable()threwanexceptionatorg.…
    2025-04-161
  • Resteasy Content-Type 默认值

    ResteasyContent-Typedefaults我正在使用Resteasy编写一个可以返回JSON和XML的应用程序,但可以选择默认为XML。这是我的方法:@GET@Path("/content")@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})publicStringcontentListRequestXm…
    2025-04-161
  • 代码不会停止运行,在 Java 中

    thecodedoesn'tstoprunning,inJava我正在用Java解决项目Euler中的问题10,即"Thesumoftheprimesbelow10is2+3+5+7=17.Findthesumofalltheprimesbelowtwomillion."我的代码是packageprojecteuler_1;importjava.math.BigInteger;importjava…
    2025-04-161
  • Out of memory java heap space

    Outofmemoryjavaheapspace我正在尝试将大量文件从服务器发送到多个客户端。当我尝试发送大小为700mb的文件时,它显示了"OutOfMemoryjavaheapspace"错误。我正在使用Netbeans7.1.2版本。我还在属性中尝试了VMoption。但仍然发生同样的错误。我认为阅读整个文件存在一些问题。下面的代码最多可用于300mb。请给我一些建议。提前致谢publicc…
    2025-04-161
  • Log4j 记录到共享日志文件

    Log4jLoggingtoaSharedLogFile有没有办法将log4j日志记录事件写入也被其他应用程序写入的日志文件。其他应用程序可以是非Java应用程序。有什么缺点?锁定问题?格式化?Log4j有一个SocketAppender,它将向服务发送事件,您可以自己实现或使用与Log4j捆绑的简单实现。它还支持syslogd和Windows事件日志,这对于尝试将日志输出与来自非Java应用程序…
    2025-04-161