generating swagger.json with backwardly compatible enum using serializeAsV2

谁说胖子不能爱 提交于 2020-04-14 07:27:41

问题


Recently I upgraded my api from netcore2.1 to netcore3.1

I was hoping not to ask the api users to re-write their client.

Thus I expect I need the swagger.json to be backwardly compatible.

In the old swagger.json an enum would look like

But now it looks like

I am using Swashbuckle.AspNetCore 5.2.1

I have an extension called AddSwaggerDocumentation and it calls

services.AddSwaggerGen(c =>
            {
            c.EnableAnnotations();
            c.ParameterFilter<SwaggerEnumParameterFilter>();
            c.SchemaFilter<SwaggerEnumFilter>();

where

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
            });
        }

    }
}

also I have

    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;
    }

and

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
            });
        }
    }
}

and

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
        });
    }
}

[update]

Trying BlueJayke's suggestion


回答1:


yeash yeash yeash... just do something like the following:

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)}";
    }
}

and lets not forgot

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
                });
        }
    }
}

and parameter file:

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 });
    }
}

hope that helps clear things up



来源:https://stackoverflow.com/questions/61022795/generating-swagger-json-with-backwardly-compatible-enum-using-serializeasv2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!