RESTful API версии и группировки в документе от Swagger с несколькими конечными точками

Я пытаюсь реализовать параметры версии в приложении MVC dotnet Core, в котором есть конечная точка API.

Установка, которую я ищу, похожа на это

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

У меня это в основном работает, но элементы на v1 не отображаются на v2. Вывод как то так

введите описание изображения здесь введите описание изображения здесь

Но когда мы дойдем до версии 2 на AiM v2 конечная точка у меня только один элемент введите описание изображения здесь

Что не то, что я ожидал

Я сделал тест, чтобы каждый из них отображался на разных страницах в чванстве

В контроллере

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

}

И в Startup.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");
        });
    }

И в index.html для файла шаблона документа Swagger UI имеет

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

});

Чтобы получить предметы на разных конечных точках, я использовал [ApiExplorerSettings(GroupName = "aim_v1")] на классы и сопоставить их в файлах Startup.cs и index.html. На данный момент я не уверен, где сделать мое редактирование, чтобы получить все [ApiVersion("1.0")] предметы показывают на [ApiVersion("2.0")] как я думаю ApiExplorerSettings GroupName это то, что он блокирует это.

2 ответа

Решение

Чтобы все было гладко интегрировано, вам также необходимо добавить официальный пакет API Explorer для управления версиями API. Это позволит сопоставить всю информацию о версии API для вас так, как это поймет Swagger. В официальной вики-теме по интеграции Swagger/Swashbuckle есть дополнительные подробности и примеры.

Настройка будет выглядеть так:

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

Полный рабочий ответ заключается в этом вопросе:группировка и управление версиями не работают вместе в swagger в веб-API asp.net core 3.1

Как сказал автор, DocInclusionPredicate в AddSwaggerGen в ConfigureServices выполняет трюк, чтобы сопоставить правильный контроллер с желаемым файлом swagger.

Другие вопросы по тегам