14 Swashbuckle Integration
Chris Martinez редактировал(а) эту страницу 2022-12-30 14:34:58 -08:00

Although the API explorers for API versioning provide all of the necessary information, there is select information that OpenAPI (formerly Swagger) and Swashbuckle will not wire up for you. This includes iterating through all the available API versions so that they don't have to be imperatively declared and changed one at a time. Fortunately, bridging this gap is really easy to achieve using Swashbuckle's extensibility model. The following are simple IOperationFilter implementations that leverage the metadata provided by the corresponding API explorer to fill in these gaps.

ASP.NET Web API or ASP.NET Web API with OData

Remember to add the necessary references to one or both of the following:

public class SwaggerDefaultValues : IOperationFilter
{
    public void Apply(
        Operation operation,
        SchemaRegistry schemaRegistry,
        ApiDescription apiDescription )
    {
        operation.deprecated |= apiDescription.IsDeprecated();

        if ( operation.parameters == null )
        {
            return;
        }

        foreach ( var parameter in operation.parameters )
        {
            var description = apiDescription.ParameterDescriptions
                                            .First( p => p.Name == parameter.name );

            parameter.description ??= description.Documentation;
            parameter.@default ??= description.ParameterDescriptor?.DefaultValue;
        }
    }
}

Use MultipleApiVersions to iterate over each ApiDescription and collate them by their corresponding group. The default group name for each ApiDescription is the formatted API version that is associated with it.

configuration.EnableSwagger(
    "{apiVersion}/swagger",
    swagger =>
    {
        swagger.MultipleApiVersions(
            ( apiDescription, version ) => apiDescription.GetGroupName() == version,
            info =>
            {
                foreach ( var group in apiExplorer.ApiDescriptions )
                {
                    info.Version( group.Name, $"Example API {group.ApiVersion}" )
                        .Description( "An example API" );
                }
            } );
        swagger.OperationFilter<SwaggerDefaultValues>();
    } )
    .EnableSwaggerUi( swagger => swagger.EnableDiscoveryUrlSelector() );

ASP.NET Core or ASP.NET Core with OData

Remember to add the necessary references to one or both of the following:

public class SwaggerDefaultValues : IOperationFilter
{
  public void Apply( OpenApiOperation operation, OperationFilterContext context )
  {
    var apiDescription = context.ApiDescription;

    operation.Deprecated |= apiDescription.IsDeprecated();

    foreach ( var responseType in context.ApiDescription.SupportedResponseTypes )
    {
        var responseKey = responseType.IsDefaultResponse
                          ? "default"
                          : responseType.StatusCode.ToString();
        var response = operation.Responses[responseKey];

        foreach ( var contentType in response.Content.Keys )
        {
            if ( !responseType.ApiResponseFormats.Any( x => x.MediaType == contentType ) )
            {
                response.Content.Remove( contentType );
            }
        }
    }

    if ( operation.Parameters == null )
    {
        return;
    }

    foreach ( var parameter in operation.Parameters )
    {
        var description = apiDescription.ParameterDescriptions
                                        .First( p => p.Name == parameter.Name );

        parameter.Description ??= description.ModelMetadata?.Description;

        if ( parameter.Schema.Default == null && description.DefaultValue != null )
        {
            var json = JsonSerializer.Serialize(
                description.DefaultValue,
                description.ModelMetadata.ModelType );
            parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson( json );
        }

        parameter.Required |= description.IsRequired;
    }
  }
}

We also need a way to tell Swashbuckle about the API versions in the application:

public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
    private readonly IApiVersionDescriptionProvider provider;

    public ConfigureSwaggerOptions( IApiVersionDescriptionProvider provider ) => this.provider = provider;

    public void Configure( SwaggerGenOptions options )
    {
        foreach ( var description in provider.ApiVersionDescriptions )
        {
            options.SwaggerDoc(
                description.GroupName,
                new OpenApiInfo()
                {
                    Title = "Example API",
                    Description = "An example API",
                    Version = description.ApiVersion.ToString(),
                } );
        }
    }
}

Now we can put it all together:

var builder = WebApplication.CreateBuilder( args );

builder.Services.AddControllers();
builder.Services.AddApiVersioning().AddMvc().AddApiExplorer();
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
builder.Services.AddSwaggerGen( options => options.OperationFilter<SwaggerDefaultValues>() );

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI(
    options =>
    {
        foreach ( var description in app.DescribeApiVersions() )
        {
            options.SwaggerEndpoint(
                $"/swagger/{description.GroupName}/swagger.json",
                description.GroupName );
        }
    } );

app.MapControllers();
app.Run();

Examples

There are end-to-end examples using API versioning and Swashbuckle: