Is it possible to expose the same Swagger JSON in both Swagger 2.0 and Open API 3 formats with Swashbuckle in ASP .NET Core?

Viewed 469

We have a legacy application that only works with Swagger 2.0 JSON format. For everything else we would like to use Open API format.

Is there any way with Swashbuckle .NET Core to expose JSON in different formats under separate URLs? It looks like the SerializeAsV2 property in the UseSwagger method options is global for all endpoints.

Basically I would like to have the following end points that contain the same API data in different formats.

/swagger/v1/openapi/swagger.json
/swagger/v1/swagger2/swagger.json
2 Answers

An alternative approach is to split the request pipeline:

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

You can serialize the OpenAPI document as V2 and serve it yourself. Taking SwaggerMiddleware as reference:

First register SwaggerGenerator:

services.AddTransient<SwaggerGenerator>();

Then inject SwaggerGenerator and build the document. Serve it from an endpoint or a controller. You can take the version as a path parameter to decide which version to serve.

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

When you visit /swagger/v1/openapi.v2.json, you'll get the OpenAPI doc serialized as v2.

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

Whereas /swagger/v1/openapi.v3.json will give you the doc serialized as v3:

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