【问题标题】:generating swagger.json with backwardly compatible enum using serializeAsV2使用 serializeAsV2 生成具有向后兼容枚举的 swagger.json
【发布时间】:2020-04-04 01:41:29
【问题描述】:

最近我将我的 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 的建议

【问题讨论】:

标签: openapi swashbuckle.aspnetcore


【解决方案1】:

是啊是啊是啊...只需执行以下操作:

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

希望能帮助解决问题

【讨论】:

  • 谢谢,我尝试按照这些思路更新了问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多