简体   繁体   English

具有多个端点的 Swagger 文档中的 RESTful api 版本控制和分组

[英]RESTful api versioning and grouping in doc from Swagger with multiple endpoints

I am trying to implement the version options on a MVC dotnet Core app that has API endpoint on it.我正在尝试在具有 API 端点的 MVC dotnet Core 应用程序上实现版本选项。

The set up i am after is like this我所追求的设置是这样的

 --AiM api
   |_v1
   |_v2
 --RMS api
   |_v1

I have it mostly working but the items on v1 are not showing up on v2.我大部分时间都在工作,但 v1 上的项目没有显示在 v2 上。 The output is like so输出是这样的

在此处输入图片说明 在此处输入图片说明

But when we get to the version 2 on the AiM v2 endpoint I only the one item但是当我们到达AiM v2端点上的版本 2 时,我只有一项在此处输入图片说明

Which is not what i was expecting这不是我所期望的

I have made a test to get each one showing on its different pages in swagger like this我做了一个测试,让每个人都像这样大摇大摆地显示在不同的页面上

In controller控制器中

[ApiVersion("2.0")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "aim_v1")]
[Route("aim/v{version:apiVersion}/write/")]
public class aimWriter_v1Controller : Controller
{

    [SwaggerOperation(Tags = new[] { "AiM Departments" })]
    [HttpPost("departments/delete/{id}")]
    public IActionResult departments(string foo)
    {
        return Json(new
        {
            results = "edited"
        });
    }

    [SwaggerOperation(Tags = new[] { "AiM Contacts" })]
    [HttpPost("contacts/delete/{id}")]
    public IActionResult contact_delete(string foo)
    {
        return Json(new
        {
            results = "edited"
        });
    }

    [SwaggerOperation(Tags = new[] { "AiM Contacts" })]
    [HttpPost("contacts/activate/{id}")]
    public IActionResult contact_activate(string foo)
    {
        return Json(new
        {
            results = "edited"
        });
    }
}


[ApiVersion("2.0")]
[ApiExplorerSettings(GroupName = "aim_v2")]
[Route("aim/v{version:apiVersion}/write/")]
public class aimWriter_v2Controller : Controller
{

    [SwaggerOperation(Tags = new[] { "AiM Contacts" })]
    [HttpPost("contacts/delete/{id}")]
    public IActionResult contact_delete(string foo)
    {
        return Json(new
        {
            results = "edited"
        });
    }


}


[ApiVersion("2.0")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "aim_v1")]
[Route("aim/v{version:apiVersion}/")]
public class aim_v1Controller : Controller
{

    [SwaggerOperation(Tags = new[] { "AiM Rooms" })]
    [HttpPost("rooms")]
    public IActionResult rooms(string foo)
    {
        return Json(new
        {
            results = "foo"
        });
    }

    [SwaggerOperation(Tags = new[] { "AiM Buildings" })]
    [HttpPost("buildings/rooms/{id}")]
    public IActionResult building_rooms(string foo)
    {
        return Json(new
        {
            results = "foo"
        });
    }

    [SwaggerOperation(Tags = new[] { "AiM Rooms" })]
    [HttpPost("rooms/{id}")]
    public IActionResult room(string foo)
    {
        return Json(new
        {
            results = "foo"
        });
    }
}

// set up as just a new endpoint (NOTE: in different controller)
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "rms_v1")]
[Route("rms/v{version:apiVersion}/")]
public class rms_v1Controller : Controller
{
    [SwaggerOperation(Tags = new[] { "RMS Orders" })]
    [HttpPost("set_order/{id}")]
    public IActionResult set_order(string foo)
    {
        return Json(new
        {
            results = "foo"
        });
    }

}

And in the Startup.csStartup.cs 中

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRouting(options => options.LowercaseUrls = true);
        services.AddMvc();

        services.AddApiVersioning(options => {
            options.AssumeDefaultVersionWhenUnspecified = true ;
            options.DefaultApiVersion = new ApiVersion(new DateTime(2016, 7, 1));
        });
        services.AddSwaggerGen(c =>
        {


            c.SwaggerDoc("aim_v1", new Info
            {
                Version = "aim/v1",
                Title = "WSU HTTP API"
            });
            c.SwaggerDoc("aim_v2", new Info
            {
                Version = "aim/v2",
                Title = "WSU HTTP API v2"
            });
            c.SwaggerDoc("rms_v1", new Info
            {
                Version = "rms/v1",
                Title = "WSU HTTP API"
            });




            //Set the comments path for the swagger json and ui.
            var basePath = PlatformServices.Default.Application.ApplicationBasePath;
            var xmlPath = Path.Combine(basePath, "project.in.bin.def.xml");
            c.IncludeXmlComments(xmlPath);
        });

    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {

        // Enable middleware to serve generated Swagger as a JSON endpoint.
        app.UseSwagger(o =>
        {
            o.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.Host = httpReq.Host.Value);
            o.RouteTemplate = "doc/{documentName}/scheme.json";
        });

        // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
        app.UseSwaggerUI(c =>
        {
            c.RoutePrefix = "docs";
            c.SwaggerEndpoint("/doc/aim_v1/scheme.json", "AiM v1.0.0");
            c.SwaggerEndpoint("/doc/rms_v1/scheme.json", "Rms v1.0.0");
            c.SwaggerEndpoint("/doc/aim_v2/scheme.json", "AiM v2.0.0");
        });
    }

And in the index.html for the swagger ui doc template file has在 swagger ui doc 模板文件的index.html中有

<script type="text/javascript">
    window.JSConfig = JSON.parse('{"SwaggerEndpoints":[{"Url":"/doc/aim_v1/scheme.json","Description":"AiM v1.0.0"},{"Url":"/doc/aim_v2/scheme.json","Description":"AiM v2.0.0"},{"Url":"/doc/rms_v1/scheme.json","Description":"RMS v1.0.0"}],"BooleanValues":["false","true"],"DocExpansion":"list","SupportedSubmitMethods":["get","post","put","delete","patch"],"OnCompleteScripts":[],"OnFailureScripts":[],"ShowRequestHeaders":false,"JsonEditor":false,"OAuth2ClientId":"your-client-id","OAuth2ClientSecret":"your-client-secret-if-required","OAuth2Realm":"your-realms","OAuth2AppName":"your-app-name","OAuth2ScopeSeparator":" ","OAuth2AdditionalQueryStringParams":{}}');

$(function () {

  hljs.configure({
    highlightSizeThreshold: 5000
  });

  // Pre load translate...
  if(window.SwaggerTranslator) {
    window.SwaggerTranslator.translate();
  }
  window.swaggerUi = new SwaggerUi({
      url: "/doc/aim_v1/scheme.json",
    dom_id: "swagger-ui-container",
    supportedSubmitMethods: ['get', 'post'],
    onComplete: function(swaggerApi, swaggerUi){
      if(typeof initOAuth == "function") {
        initOAuth({
            clientId: "ffff==",
            clientSecret: "bbbb",
            realm: "wsu-api",
            appName: "wsu-api-broker",
            scopeSeparator: " ",
            additionalQueryStringParams: {}
        });
      }

      if(window.SwaggerTranslator) {
        window.SwaggerTranslator.translate();
      }
      _.each(JSConfig.OnCompleteScripts, function (script) {
          $.getScript(script);
      });

    },
    onFailure: function(data) {
      log("Unable to Load SwaggerUI");
    },
    docExpansion: false,
    jsonEditor: false,
    defaultModelRendering: 'schema',
    showRequestHeaders: false
  });

  window.swaggerUi.load();

  function log() {
    if ('console' in window) {
      console.log.apply(console, arguments);
    }
  }

}); });

In order to get the items on the different endpoints I used the [ApiExplorerSettings(GroupName = "aim_v1")] on the classes and matched them up in the Startup.cs and index.html files.为了获取不同端点上的项目,我在类上使用了[ApiExplorerSettings(GroupName = "aim_v1")]并将它们匹配到Startup.csindex.html文件中。 At this point I am unsure where to make my edit to get all of the [ApiVersion("1.0")] items show on the [ApiVersion("2.0")] as I think the ApiExplorerSettings GroupName is what it locking this up.在这一点上,我不确定在哪里进行编辑以使所有[ApiVersion("1.0")]项目显示在[ApiVersion("2.0")]因为我认为ApiExplorerSettings GroupName是它锁定的原因。

To integrate everything smoothly, you also need to add the official API Explorer package for API Versioning.为了顺利集成所有内容,您还需要添加用于 API Versioning 的官方API Explorer包。 This will collate all of the API version information for you in a way that Swagger will understand.这将以 Swagger 能够理解的方式为您整理所有 API 版本信息。 The official Swagger/Swashbuckle integration wiki topic has additional details and examples.官方 Swagger/Swashbuckle 集成wiki 主题有更多详细信息和示例。

The setup will look like:设置将如下所示:

public void ConfigureServices( IServiceCollection services )
{
    // note: this option is only necessary when versioning by url segment.
    // the SubstitutionFormat property can be used to control the format of the API version
    services.AddMvcCore().AddVersionedApiExplorer(
        options =>
        {
            options.GroupNameFormat = "'v'VVV";
            options.SubstituteApiVersionInUrl = true;
        } );

    services.AddMvc();
    services.AddApiVersioning();
    services.AddSwaggerGen(
        options =>
        {
            var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();

            foreach ( var description in provider.ApiVersionDescriptions )
            {
                options.SwaggerDoc( description.GroupName, CreateInfoForApiVersion( description ) );
            }

            options.IncludeXmlComments( XmlCommentsFilePath );
        } );
}

public void Configure( IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider )
{
    app.UseMvc();
    app.UseSwagger();
    app.UseSwaggerUI(
        options =>
        {
            foreach ( var description in provider.ApiVersionDescriptions )
            {
                options.SwaggerEndpoint( $"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant() );
            }
        } );
}

static string XmlCommentsFilePath
{
    get
    {
        var basePath = PlatformServices.Default.Application.ApplicationBasePath;
        var fileName = typeof( Startup ).GetTypeInfo().Assembly.GetName().Name + ".xml";
        return Path.Combine( basePath, fileName );
    }
}

static Info CreateInfoForApiVersion( ApiVersionDescription description )
{
    var info = new Info()
    {
        Title = $"Sample API {description.ApiVersion}",
        Version = description.ApiVersion.ToString(),
        Description = "A sample application with Swagger, Swashbuckle, and API versioning.",
        Contact = new Contact() { Name = "Bill Mei", Email = "bill.mei@somewhere.com" },
        TermsOfService = "Shareware",
        License = new License() { Name = "MIT", Url = "https://opensource.org/licenses/MIT" }
    };

    if ( description.IsDeprecated )
    {
        info.Description += " This API version has been deprecated.";
    }

    return info;
}

A full working answer is in that question: Grouping and Versioning not working well together in swagger in asp.net core 3.1 web api一个完整的工作答案是在那个问题中: Grouping and Versioning not working well together in swagger in asp.net core 3.1 web api

As the author said, the DocInclusionPredicate in AddSwaggerGen in the ConfigureServices is doing the trick to map the proper controller to the wanted swagger file.正如作者所说,ConfigureServices 中 AddSwaggerGen 中的 DocInclusionPredicate 正在执行将适当的控制器映射到想要的 swagger 文件的技巧。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM