Можно ли предоставить один и тот же JSON Swagger как в форматах Swagger 2.0, так и в форматах Open API 3 с помощью Swashbuckle в ASP .NET Core?

#asp.net-core #swashbuckle.aspnetcore

Вопрос:

У нас есть устаревшее приложение, которое работает только с форматом JSON Swagger 2.0. Для всего остального мы хотели бы использовать открытый формат API.

Есть ли какой-нибудь способ с помощью Swashbuckle .NET Core предоставляет JSON в разных форматах по отдельным URL-адресам? Похоже SerializeAsV2 , что свойство в параметрах UseSwagger метода является глобальным для всех конечных точек.

В принципе, я хотел бы иметь следующие конечные точки, которые содержат одни и те же данные API в разных форматах.

 /swagger/v1/openapi/swagger.json
/swagger/v1/swagger2/swagger.json
 

Ответ №1:

Альтернативный подход заключается в разделении конвейера запросов:

 // serve v3 from /swagger/v1/swagger.json
app.UseSwagger(o => o.RouteTemplate = "swagger/{documentName}/swagger.json");

// serve v2 from /swagger2/v1/swagger.json
app.Map("/swagger2", swaggerApp => 
    swaggerApp.UseSwagger(options => {
        // note the dropped prefix "swagger/"
        options.RouteTemplate = "{documentName}/swagger.json";
        options.SerializeAsV2 = true;
    })
);
 

Комментарии:

1. Это действительно элегантно и отлично работает. Спасибо!

Ответ №2:

Вы можете сериализовать документ OpenAPI как V2 и обслуживать его самостоятельно. Принимая SwaggerMiddleware в качестве ориентира:

Первая регистрация SwaggerGenerator :

 services.AddTransient<SwaggerGenerator>();
 

Затем введите SwaggerGenerator и создайте документ. Обслуживайте его с конечной точки или контроллера. Вы можете использовать версию в качестве параметра пути, чтобы решить, какую версию обслуживать.

 app.UseEndpoints(e =>
{
    // ...
    e.MapGet("/swagger/{documentName}/swagger.{version}.json", context =>
    {
        var documentName = context.Request.RouteValues.GetValueOrDefault("documentName", null) as string;
        var version = context.Request.RouteValues.GetValueOrDefault("version", null) as string;
        if (documentName is null || version is null)
        {
            context.Response.StatusCode = StatusCodes.Status400BadRequest;
            return Task.CompletedTask;
        }

        // inject SwaggerGenerator
        var swaggerGenerator = context.RequestServices.GetRequiredService<SwaggerGenerator>();
        var doc = swaggerGenerator.GetSwagger(documentName);

        // serialize the document as json
        using var writer = new StringWriter(CultureInfo.InvariantCulture);
        var serializer = new OpenApiJsonWriter(writer);
        if (version == "v2")
        {
            doc.SerializeAsV2(serializer);
        }
        else
        {
            doc.SerializeAsV3(serializer);
        }
        var json = writer.ToString();

        // serve it as json
        context.Response.ContentType = MediaTypeNames.Application.Json;
        return context.Response.WriteAsync(json, new UTF8Encoding(false));
    });
});
 

При посещении вы /swagger/v1/openapi.v2.json получите документ OpenAPI , сериализованный как версия 2.

 {
  "swagger": "2.0",
  "info": {
    "title": "ApiPlayground",
    "version": "1.0"
  },
  "paths": { ... }
}
 

В то /swagger/v1/openapi.v3.json время как вам выдадут документ, сериализованный как v3:

 {
  "openapi": "3.0.1",
  "info": {
    "title": "ApiPlayground",
    "version": "1.0"
  },
  "paths": { ... },
  "components": { ... }
}
 

Комментарии:

1. Мне все еще нужно вызывать UseSwagger() метод? Спасибо за подробный пример.

2. Нет, он может остаться, или вы можете удалить его совсем. Но вам может потребоваться обновить SwaggerUI конфигурацию промежуточного программного обеспечения. app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.v2.json", "ApiPlayground v1"));