генерирование swagger. json с обратно совместимым enum с использованием serializeAsV2 - PullRequest
2 голосов
/ 04 апреля 2020

Недавно я обновил свой API с netcore2.1 до netcore3.1

Я надеялся не просить пользователей API переписать свой клиент.

Таким образом, я ожидаю, что мне нужно swagger. json для обратной совместимости.

В старом swagger. json перечисление будет выглядеть как

enumerated strings

Но теперь это выглядит так:

$ref

Я использую 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
        });
    }
}

header type code

[обновление]

Попытка предложения BlueJayke

Definitions replacement

1 Ответ

1 голос
/ 13 апреля 2020

да sh да sh да sh ... просто сделайте что-то вроде следующего:

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

надеюсь, что это поможет прояснить ситуацию

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...