In an earlier version of Swashbuckle.AspNetCore, SwaggerGenOptions came with two extensions method that helped facilitate returning camelCase values from your enum data types.
services.AddSwaggerGen(c => | |
{ | |
c.DescribeAllEnumsAsStrings(); | |
c.DescribeStringEnumsInCamelCase(); | |
}); |
However, in newer versions of Swashbuckle, those methods have been deprecated and they ask you to use the built-in features of your preferred JsonSerializer (this example is for Newtonsoft’s Json.NET):
services.AddMvc() | |
.AddNewtonsoftJson(options => { | |
var strategy = new CamelCaseNamingStrategy(); | |
options.SerializerSettings.Converters.Add(new StringEnumConverter() { NamingStrategy = strategy }); | |
}); |
These options will produce the expected camelCased data to flow across the wire. But, it won’t update the documentation generated by Swashbuckle to show the camelCased values. Instead Swashbuckle will continue to show the enum values:
The Unchase.Swashbuckle.AspNetCore.Extensions collection has an Enum’s extension which will provide the documentation that you’re looking for, but it still expects you to return numerical values (instead of the camelCased names).
I was looking for both:
- Return camelCased values across the wire, and
- Update the swagger/OpenApi documentation to display the camelCased values
Of course, I couldn’t be the only one looking for something like this and there was a nice piece of starting code on stackoverflow (Swagger UI Web Api documentation Present enums as strings), which grew into an EnumDocumentFilter.cs class (its specific to Newtonsoft’s Json.NET). The document filter produces output that looks like this:
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Reflection.Metadata; | |
using System.Text.Json; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.Extensions.Options; | |
using Microsoft.OpenApi.Any; | |
using Microsoft.OpenApi.Models; | |
using Newtonsoft.Json.Converters; | |
using Newtonsoft.Json.Serialization; | |
using Swashbuckle.AspNetCore.SwaggerGen; | |
namespace YourNamespace | |
{ | |
/// <summary> | |
/// https://stackoverflow.com/questions/36452468/swagger-ui-web-api-documentation-present-enums-as-strings | |
/// </summary> | |
public class EnumDocumentFilter : IDocumentFilter | |
{ | |
private MvcNewtonsoftJsonOptions _jsonOptions; | |
private bool _isCamelCase = true; | |
public EnumDocumentFilter( | |
IOptions<MvcNewtonsoftJsonOptions> jsonOptions | |
) { | |
_jsonOptions = jsonOptions.Value; | |
var stringEnumConvertor = _jsonOptions.SerializerSettings.Converters.FirstOrDefault(c => | |
{ | |
return c.GetType() == typeof(StringEnumConverter); | |
}) as StringEnumConverter; | |
if (stringEnumConvertor != null) | |
{ | |
_isCamelCase = stringEnumConvertor.NamingStrategy.GetType() == typeof(CamelCaseNamingStrategy); | |
} | |
} | |
public void Apply(OpenApiDocument openApiDoc, DocumentFilterContext context) | |
{ | |
var schemas = openApiDoc.Components.Schemas; | |
foreach (var schemaNode in schemas.Where(x => x.Value?.Enum?.Count > 0)) | |
{ | |
IList<IOpenApiAny> propertyEnums = schemaNode.Value.Enum; | |
if (propertyEnums != null && propertyEnums.Count > 0) | |
{ | |
var name = schemaNode.Key; | |
var schema = schemaNode.Value; | |
schema.Description += DescribeEnum(propertyEnums, name, context); | |
schema.Enum = GenerateOpenApiEnumAsString(name, context); | |
schema.Type = "string"; | |
schema.Format = null; | |
} | |
} | |
// add enum descriptions to input parameters | |
foreach (var pathItem in openApiDoc.Paths.Values) | |
{ | |
DescribeEnumParameters(pathItem.Operations, openApiDoc, context); | |
} | |
} | |
public List<IOpenApiAny> GenerateOpenApiEnumAsString(string typeName, DocumentFilterContext context) | |
{ | |
var type = GetEnumTypeByName(typeName, context); | |
var enumNames = Enum.GetNames(type); | |
var openApiEnumNames = new List<IOpenApiAny>(); | |
foreach (var n in enumNames) | |
{ | |
var formattedName = FormatName(n); | |
openApiEnumNames.Add(new OpenApiString(formattedName)); | |
} | |
return openApiEnumNames; | |
} | |
private string FormatName(string name) | |
{ | |
if (!_isCamelCase) return name; | |
if (name.Length == 1) return name.ToLower(); | |
var camelCase = name.Substring(0, 1).ToLower() + | |
name.Substring(1); | |
return camelCase; | |
} | |
private void DescribeEnumParameters( | |
IDictionary<OperationType, OpenApiOperation> operations, | |
OpenApiDocument openApiDoc, | |
DocumentFilterContext context | |
) { | |
if (operations != null) | |
{ | |
foreach (var oper in operations) | |
{ | |
foreach (var param in oper.Value.Parameters) | |
{ | |
var schemaNode = openApiDoc.Components.Schemas.FirstOrDefault(x => x.Key == param.Name); | |
if (schemaNode.Value != null) | |
{ | |
var name = schemaNode.Key; | |
var schema = schemaNode.Value; | |
param.Description += DescribeEnum(schema.Enum, name, context); | |
param.Schema.Enum = GenerateOpenApiEnumAsString(name, context); | |
schema.Type = "string"; | |
schema.Format = null; | |
} | |
} | |
} | |
} | |
} | |
private Type GetEnumTypeByName(string enumTypeName, DocumentFilterContext context) | |
{ | |
var matches = AppDomain.CurrentDomain | |
.GetAssemblies() | |
.SelectMany(x => x.GetTypes()) | |
.Where(x => enumTypeName.EndsWith(x.Name )); | |
foreach(var m in matches) | |
{ | |
string schemaId; | |
context.SchemaRepository.TryGetIdFor(m, out schemaId); | |
if (schemaId == enumTypeName) return m; | |
} | |
throw new ArgumentException(); | |
} | |
private string DescribeEnum(IList<IOpenApiAny> enums, string proprtyTypeName, DocumentFilterContext context) | |
{ | |
List<string> enumDescriptions = new List<string>(); | |
var enumType = GetEnumTypeByName(proprtyTypeName, context); | |
if (enumType == null) | |
return null; | |
foreach (OpenApiInteger enumOption in enums) | |
{ | |
int enumInt = enumOption.Value; | |
var name = Enum.GetName(enumType, enumInt); | |
var formattedName = FormatName(name); | |
enumDescriptions.Add(string.Format("{0} = {1}", enumInt, formattedName)); | |
} | |
return string.Join(", ", enumDescriptions.ToArray()); | |
} | |
} | |
} |
0 comments:
Post a Comment