Skip to content

Instantly share code, notes, and snippets.

@vpetkovic
Last active September 9, 2020 14:31
Show Gist options
  • Save vpetkovic/1ba8d9298e4bba411df697d21e379806 to your computer and use it in GitHub Desktop.
Save vpetkovic/1ba8d9298e4bba411df697d21e379806 to your computer and use it in GitHub Desktop.
[NETCore 3.1]
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace SwaggerVersioning
{
// <summary>
/// Configures the Swagger generation options.
/// </summary>
/// <remarks>
/// <para>
/// This allows API versioning to define a Swagger document per API version after the
/// <see cref="IApiVersionDescriptionProvider" /> service has been resolved from the service container.
/// </para>
/// <para>Taken from https://github.com/microsoft/aspnet-api-versioning.</para>
/// </remarks>
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
#region member vars
private readonly IApiVersionDescriptionProvider _provider;
#endregion
#region constructors and destructors
/// <summary>
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions" /> class.
/// </summary>
/// <param name="provider">
/// The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger
/// documents.
/// </param>
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider)
{
_provider = provider;
}
#endregion
#region explicit interfaces
/// <inheritdoc />
public void Configure(SwaggerGenOptions options)
{
// add a swagger document for each discovered API version
// note: you might choose to skip or document deprecated API versions differently
foreach (var description in _provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
}
#endregion
#region methods
/// <summary>
/// Internal implementation for building the Swagger basic config.
/// </summary>
/// <param name="description">The description object containing the.</param>
/// <returns>The generated Open API info.</returns>
private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var info = new OpenApiInfo
{
Title = "Title",
Version = description.ApiVersion.ToString(),
Description = @"",
};
if (description.IsDeprecated)
{
info.Description += @"<p><strong><span style=""color:white;background-color:red"">VERSION IS DEPRECATED</span></strong></p>";
}
return info;
}
#endregion
}
// Add security lock for individual methods that require authorization
public class SecurityRequirementsOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
var allowAnonymousAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AllowAnonymousAttribute>();
if (authAttributes.Any() && !allowAnonymousAttributes.Any())
{
operation.Security.Add(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "OAuth2",
Name = "Bearer",
In = ParameterLocation.Header,
}, new List<string>()
}
});
//operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
//operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
}
}
}
}
public void ConfigureServices(IServiceCollection services)
{
#region API Versioning
services.AddApiVersioning(o =>
{
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
o.ReportApiVersions = true;
});
services.AddVersionedApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
#endregion
#region API Documentation (Swagger)
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddSwaggerGen(
options =>
{
options.OperationFilter<SecurityRequirementsOperationFilter>();
// integrate xml comments
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
#endregion
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");
c.SwaggerEndpoint("/swagger/v2/swagger.json", "API V2");
c.RoutePrefix = "docs";
c.InjectStylesheet("/swagger/ui/custom.css");
});
}
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;
namespace SwaggerVersioning
{
public class SwaggerVersioning
{
public class RemoveVersionFromParameter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var versionParameter = operation.Parameters.Single(p => p.Name == "version");
operation.Parameters.Remove(versionParameter);
}
}
public class ReplaceVersionWithExactValueInPath : IDocumentFilter
{
// https://stackoverflow.com/questions/60563647/migrate-code-2-0-to-3-1-core-code-then-swagger-api-versing-not-working
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var paths = new OpenApiPaths();
foreach (var path in swaggerDoc.Paths)
{
paths.Add(path.Key.Replace("v{version}", swaggerDoc.Info.Version), path.Value);
}
swaggerDoc.Paths = paths;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment