создание swagger.json с обратно совместимым перечислением с использованием serializeAsV2
Недавно я обновил свой api с netcore2.1 до netcore3.1
Я надеялся не просить пользователей api переписывать своего клиента.
Таким образом, я полагаю, что мне нужно, чтобы swagger.json был обратно совместим.
В старом swagger.json перечисление выглядело бы как
Но теперь похоже
Я использую Swashbuckle.AspNetCore 5.2.1
У меня есть расширение AddSwaggerDocumentation, которое вызывает
services.AddSwaggerGen(c =>
{
c.EnableAnnotations();
c.ParameterFilter<SwaggerEnumParameterFilter>();
c.SchemaFilter<SwaggerEnumFilter>();
где
public class SwaggerEnumFilter : ISchemaFilter
{
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
var values = Enum.GetValues(context.Type);
var valuesArr = new OpenApiArray();
foreach (var value in values)
{
var item = new OpenApiObject
{
["name"] = new OpenApiString(Enum.GetName(context.Type, value)),
["value"] = new OpenApiString(value.ToString())
};
valuesArr.Add(item);
}
model.Extensions.Add("x-ms-enum", new OpenApiObject
{
["name"] = new OpenApiString(context.Type.Name),
["modelAsString"] = new OpenApiBoolean(true),
["values"] = valuesArr
});
}
}
}
также у меня есть
public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
{
var basePath = "/v1";
app.UseSwagger(c =>
{
c.RouteTemplate = "api-docs/{documentName}/swagger.json";
c.SerializeAsV2 = true;
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}{basePath}" } };
});
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("./v1/swagger.json", "Versioned API v1.0"); //
c.RoutePrefix = "api-docs";
});
return app;
}
а также
public class SwaggerEnumParameterFilter : IParameterFilter
{
public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
{
var type = context.ApiParameterDescription.Type;
if (type.IsEnum)
{
var values = Enum.GetValues(type);
var valuesArr = new OpenApiArray();
foreach (var value in values)
{
var item = new OpenApiObject
{
["name"] = new OpenApiString(Enum.GetName(type, value)),
["value"] = new OpenApiString(value.ToString())
};
valuesArr.Add(item);
}
parameter.Extensions.Add("x-ms-enum", new OpenApiObject
{
["name"] = new OpenApiString(type.Name),
["modelAsString"] = new OpenApiBoolean(true),
["values"] = valuesArr
});
}
}
}
а также
public static class SwaggerGenOptionsExtensions
{
public static SwaggerGenOptions RegisterEnumSchemas(this SwaggerGenOptions options, Assembly assembly, string enumsNamespace)
{
var enums = from t in assembly.GetTypes()
where t.IsEnum && t.Namespace == enumsNamespace
select t;
foreach (var enumerate in enums)
{
var nullableEnumerate = typeof(Nullable<>).MakeGenericType(enumerate);
MapEnumType(options, enumerate, false);
MapEnumType(options, nullableEnumerate, true);
}
return options;
}
private static void MapEnumType(SwaggerGenOptions options, Type enumerate, bool nullable)
{
var underlyingEnum = nullable ? Nullable.GetUnderlyingType(enumerate) : enumerate;
options.MapType(enumerate, () => new OpenApiSchema
{
Type = "string",
Enum = underlyingEnum.GetEnumNames().Select(name => new OpenApiString(name)).Cast<IOpenApiAny>().ToList(),
Nullable = nullable
});
}
}
[Обновить]
Попытка предложения BlueJayke
1 ответ
Yeash yeash yeash... просто сделайте что-нибудь вроде следующего:
public class EnumDocumentFilter : IDocumentFilter {
/// <inheritdoc />
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) {
// add enum descriptions to result models
foreach (var schemaDictionaryItem in swaggerDoc.Definitions) {
var schema = schemaDictionaryItem.Value;
foreach (var propertyDictionaryItem in schema.Properties) {
var property = propertyDictionaryItem.Value;
var propertyEnums = property.Enum;
if (propertyEnums != null && propertyEnums.Count > 0) {
property.Description += DescribeEnum(propertyEnums);
}
}
}
if (swaggerDoc.Paths.Count <= 0) return;
// add enum descriptions to input parameters
foreach (var pathItem in swaggerDoc.Paths.Values) {
DescribeEnumParameters(pathItem.Parameters);
// head, patch, options, delete left out
var possibleParameterisedOperations = new List<Operation> {pathItem.Get, pathItem.Post, pathItem.Put};
possibleParameterisedOperations.FindAll(x => x != null)
.ForEach(x => DescribeEnumParameters(x.Parameters));
}
}
private static void DescribeEnumParameters(IList<IParameter> parameters) {
if (parameters == null) return;
foreach (var param in parameters) {
if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true) {
param.Description += DescribeEnum(nbParam.Enum);
} else if (param.Extensions.ContainsKey("enum") && param.Extensions["enum"] is IList<object> paramEnums &&
paramEnums.Count > 0) {
param.Description += DescribeEnum(paramEnums);
}
}
}
private static string DescribeEnum(IEnumerable<object> enums) {
var enumDescriptions = new List<string>();
Type type = null;
foreach (var enumOption in enums) {
if (type == null) type = enumOption.GetType();
enumDescriptions.Add($"{Convert.ChangeType(enumOption, type.GetEnumUnderlyingType())} = {Enum.GetName(type, enumOption)}");
}
return $"{Environment.NewLine}{string.Join(Environment.NewLine, enumDescriptions)}";
}
}
и давай не забываем
public class EnumTypeSchemaFilter : ISchemaFilter {
public void Apply(Schema schema, SchemaFilterContext context) {
var typeInfo = context.SystemType.GetTypeInfo();
if (typeInfo.IsEnum) {
schema.Extensions.Add(
"x-ms-enum",
new {
name = typeInfo.Name,
modelAsString = false
});
}
}
}
и файл параметров:
public class AutoRestParameterFilter : IParameterFilter
{
public void Apply(IParameter parameter, ParameterFilterContext context)
{
var type = context.ApiParameterDescription.Type;
if (type.IsEnum)
parameter.Extensions.Add( "x-ms-enum", new { name = type.Name, modelAsString = false });
}
}
надеюсь, что это поможет прояснить ситуацию