Last active
April 13, 2024 07:34
-
-
Save mykeels/054b88035bf1bb5be57be95fd176f939 to your computer and use it in GitHub Desktop.
Code for extracting OpenAPI schema from ASP.NET Core projects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use a conditional, because You may not want to provide swagger documentation in public environments | |
if (_configuration.GetValue<bool>("SwaggerConfiguration:EnableSwagger")) | |
{ | |
services.AddEndpointsApiExplorer(); | |
services.AddSwaggerGen(options => { | |
options.AddCustomIds(); | |
options.AddMetadata(typeof(Program)); | |
options.SchemaFilter<NullableEnumSchemaFilter>(); | |
options.SchemaFilter<RequiredPropertiesSchemaFilter>(); | |
options.SchemaFilter<EnumDescriptionSchemaFilter>(); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": 1, | |
"isRoot": true, | |
"tools": { | |
"swashbuckle.aspnetcore.cli": { | |
"version": "6.2.3", | |
"commands": [ | |
"swagger" | |
] | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
using Microsoft.OpenApi.Any; | |
using Microsoft.OpenApi.Models; | |
using Swashbuckle.AspNetCore.SwaggerGen; | |
public class EnumDescriptionSchemaFilter : ISchemaFilter | |
{ | |
public void Apply(OpenApiSchema schema, SchemaFilterContext context) | |
{ | |
if (context.Type.IsEnum) | |
{ | |
var enumStringNames = Enum.GetNames(context.Type); | |
IEnumerable<long> enumStringValues; | |
try | |
{ | |
enumStringValues = Enum.GetValues(context.Type).Cast<long>(); | |
} | |
catch | |
{ | |
enumStringValues = Enum.GetValues(context.Type).Cast<int>().Select(i => Convert.ToInt64(i)); | |
} | |
var enumStringKeyValuePairs = enumStringNames.Zip(enumStringValues, (name, value) => $"{value} = {name}"); | |
var enumStringNamesAsOpenApiArray = new OpenApiArray(); | |
enumStringNamesAsOpenApiArray.AddRange(enumStringNames.Select(name => new OpenApiString(name)).ToArray()); | |
schema.Description = string.Join("\n", enumStringKeyValuePairs); | |
schema.Extensions.Add("x-enum-varnames", enumStringNamesAsOpenApiArray); | |
schema.Extensions.Add("x-enumNames", enumStringNamesAsOpenApiArray); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Reflection; | |
using Microsoft.OpenApi.Models; | |
using Swashbuckle.AspNetCore.Swagger; | |
using Swashbuckle.AspNetCore.SwaggerGen; | |
using System.Linq; | |
using System.Collections.Generic; | |
using AutoMapper.Internal; | |
public class NullableEnumSchemaFilter : ISchemaFilter | |
{ | |
public void Apply(OpenApiSchema schema, SchemaFilterContext context) | |
{ | |
var isReferenceType = | |
TypeHelper.IsReference(context.Type) && | |
!TypeHelper.IsCLR(context.Type) && | |
!TypeHelper.IsMicrosoft(context.Type); | |
if(!isReferenceType) { return; } | |
var bindingFlags = BindingFlags.Public | BindingFlags.Instance; | |
var members = context.Type.GetFields(bindingFlags).Cast<MemberInfo>() | |
.Concat(context.Type.GetProperties(bindingFlags)) | |
.ToArray(); | |
var hasNullableEnumMembers = members.Any(x => TypeHelper.IsNullableEnum(x.GetMemberType())); | |
if (!hasNullableEnumMembers) { return; } | |
schema.Properties.Where(x => !x.Value.Nullable).ToList().ForEach(property => | |
{ | |
var name = property.Key; | |
var possibleNames = new string[] | |
{ | |
name, | |
TextCaseHelper.ToPascalCase(name), | |
}; // handle different cases | |
var sourceMember = possibleNames | |
.Select(n => context.Type.GetMember(n, bindingFlags).FirstOrDefault()) | |
.Where(x => x != null) | |
.FirstOrDefault(); | |
if (sourceMember == null) { return; } | |
var sourceMemberType = sourceMember.GetMemberType(); | |
if (sourceMemberType == null || !TypeHelper.IsNullableEnum(sourceMemberType)) { return; } | |
// manual nullability fixes | |
if (property.Value.Reference != null) | |
{ | |
// https://stackoverflow.com/a/48114924/5168794 | |
property.Value.Nullable = true; | |
property.Value.AllOf = new List<OpenApiSchema>() | |
{ | |
new OpenApiSchema | |
{ | |
Reference = property.Value.Reference, | |
}, | |
}; | |
property.Value.Reference = null; | |
} | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Reflection; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.OpenApi.Any; | |
using Microsoft.OpenApi.Interfaces; | |
using Microsoft.OpenApi.Models; | |
using Swashbuckle.AspNetCore.SwaggerGen; | |
public static class OpenApiExtensions | |
{ | |
/// <summary> | |
/// Adds custom operation and schema ids to the Swagger document. | |
/// </summary> | |
/// <param name="options"></param> | |
/// <returns></returns> | |
public static SwaggerGenOptions AddCustomIds(this SwaggerGenOptions options) | |
{ | |
options.CustomOperationIds(e => $"{e.ActionDescriptor.RouteValues["controller"]}_{e.ActionDescriptor.RouteValues["action"]}"); | |
options.CustomSchemaIds(type => type.FullName?.Replace("+", ".")); | |
return options; | |
} | |
/// <summary> | |
/// Adds metadata to the Swagger document, such as: | |
/// - Title | |
/// - Version e.g. v1 | |
/// - API Version e.g. 1.0.0, derived from the AssemblyInformationalVersionAttribute | |
/// </summary> | |
/// <param name="options"></param> | |
/// <param name="type"></param> | |
/// <returns></returns> | |
public static SwaggerGenOptions AddMetadata(this SwaggerGenOptions options, Type type) | |
{ | |
var entryAssembly = type | |
.GetTypeInfo() | |
.Assembly; | |
string projectVersion = entryAssembly | |
.GetCustomAttribute<AssemblyInformationalVersionAttribute>() | |
?.InformationalVersion ?? "1.0.0"; | |
options.SwaggerDoc( | |
"v1", | |
new OpenApiInfo | |
{ | |
Title = entryAssembly.GetName().Name, | |
Version = "v1", | |
Extensions = new Dictionary<string, IOpenApiExtension> | |
{ | |
{ "apiVersion", new OpenApiString(projectVersion) } | |
} | |
} | |
); | |
return options; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.OpenApi.Models; | |
using Swashbuckle.AspNetCore.SwaggerGen; | |
public class RequiredPropertiesSchemaFilter : ISchemaFilter | |
{ | |
public void Apply(OpenApiSchema schema, SchemaFilterContext context) | |
{ | |
if (schema.Type == "object") | |
{ | |
foreach (var openApiSchema in schema.Properties) | |
{ | |
if (openApiSchema.Value.Nullable == false) | |
{ | |
schema.Required.Add(openApiSchema.Key); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk.Web"> | |
<PropertyGroup> | |
<TargetFramework>net6.0</TargetFramework> | |
<Nullable>enable</Nullable> | |
<ImplicitUsings>enable</ImplicitUsings> | |
<Version>0.0.1</Version> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> | |
</ItemGroup> | |
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> | |
<Exec Command="dotnet tool restore" /> | |
<Exec Command="dotnet swagger tofile --output obj/swagger.json $(OutputPath)$(AssemblyName).dll v1" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" /> | |
</Target> | |
</Project> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment