Swagger UI Web Api documentation Present enums as strings?

后端 未结 20 1795
半阙折子戏
半阙折子戏 2020-11-27 11:31

Is there a way to display all enums as their string value in swagger instead of their int value?

I want to be able to submit POST actions and put enums according to

相关标签:
20条回答
  • 2020-11-27 11:44

    .Net Core 3.0

       using Newtonsoft.Json.Converters;
    
     services
        .AddMvc(options =>
        {
         options.EnableEndpointRouting = false;
         })
        .AddNewtonsoftJson(options => options.SerializerSettings.Converters.Add(new StringEnumConverter()))
    
    0 讨论(0)
  • 2020-11-27 11:45

    So I think I have a similar problem. I'm looking for swagger to generate enums along with the int -> string mapping. The API must accept the int. The swagger-ui matters less, what I really want is code generation with a "real" enum on the other side (android apps using retrofit in this case).

    So from my research this ultimately seems to be a limit of the OpenAPI specification which Swagger uses. It's not possible to specify names and numbers for enums.

    The best issue I've found to follow is https://github.com/OAI/OpenAPI-Specification/issues/681 which looks like a "maybe soon" but then Swagger would have to be updated, and in my case Swashbuckle as well.

    For now my workaround has been to implement a document filter that looks for enums and populates the relevant description with the contents of the enum.

            GlobalConfiguration.Configuration
                .EnableSwagger(c =>
                    {
                        c.DocumentFilter<SwaggerAddEnumDescriptions>();
    
                        //disable this
                        //c.DescribeAllEnumsAsStrings()
    

    SwaggerAddEnumDescriptions.cs:

    using System;
    using System.Web.Http.Description;
    using Swashbuckle.Swagger;
    using System.Collections.Generic;
    
    public class SwaggerAddEnumDescriptions : IDocumentFilter
    {
        public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
        {
            // add enum descriptions to result models
            foreach (KeyValuePair<string, Schema> schemaDictionaryItem in swaggerDoc.definitions)
            {
                Schema schema = schemaDictionaryItem.Value;
                foreach (KeyValuePair<string, Schema> propertyDictionaryItem in schema.properties)
                {
                    Schema property = propertyDictionaryItem.Value;
                    IList<object> propertyEnums = property.@enum;
                    if (propertyEnums != null && propertyEnums.Count > 0)
                    {
                        property.description += DescribeEnum(propertyEnums);
                    }
                }
            }
    
            // add enum descriptions to input parameters
            if (swaggerDoc.paths.Count > 0)
            {
                foreach (PathItem pathItem in swaggerDoc.paths.Values)
                {
                    DescribeEnumParameters(pathItem.parameters);
    
                    // head, patch, options, delete left out
                    List<Operation> possibleParameterisedOperations = new List<Operation> { pathItem.get, pathItem.post, pathItem.put };
                    possibleParameterisedOperations.FindAll(x => x != null).ForEach(x => DescribeEnumParameters(x.parameters));
                }
            }
        }
    
        private void DescribeEnumParameters(IList<Parameter> parameters)
        {
            if (parameters != null)
            {
                foreach (Parameter param in parameters)
                {
                    IList<object> paramEnums = param.@enum;
                    if (paramEnums != null && paramEnums.Count > 0)
                    {
                        param.description += DescribeEnum(paramEnums);
                    }
                }
            }
        }
    
        private string DescribeEnum(IList<object> enums)
        {
            List<string> enumDescriptions = new List<string>();
            foreach (object enumOption in enums)
            {
                enumDescriptions.Add(string.Format("{0} = {1}", (int)enumOption, Enum.GetName(enumOption.GetType(), enumOption)));
            }
            return string.Join(", ", enumDescriptions.ToArray());
        }
    
    }
    

    This results in something like the following on your swagger-ui so at least you can "see what you're doing":

    0 讨论(0)
  • 2020-11-27 11:45

    I have modified Hosam Rehani's answer to work with nullable enums and with collection of enums also. The previous answer also works only if a property is named exactly like it's type. All these problems are addressed in the code below.

    It works with .net core 3.x and swagger 5.x.

    it could be more efficient by not searching for the enum type twice in some cases.

    class SwaggerAddEnumDescriptions : IDocumentFilter
    {
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            // add enum descriptions to result models
            foreach (var property in swaggerDoc.Components.Schemas.Where(x => x.Value?.Enum?.Count > 0))
            {
                IList<IOpenApiAny> propertyEnums = property.Value.Enum;
                if (propertyEnums != null && propertyEnums.Count > 0)
                {
                    property.Value.Description += DescribeEnum(propertyEnums, property.Key);
                }
            }
    
            // add enum descriptions to input parameters
            foreach (var pathItem in swaggerDoc.Paths)
            {
                DescribeEnumParameters(pathItem.Value.Operations, swaggerDoc, context.ApiDescriptions, pathItem.Key);
            }
        }
    
        private void DescribeEnumParameters(IDictionary<OperationType, OpenApiOperation> operations, OpenApiDocument swaggerDoc, IEnumerable<ApiDescription> apiDescriptions, string path)
        {
            path = path.Trim('/');
            if (operations != null)
            {
                var pathDescriptions = apiDescriptions.Where(a => a.RelativePath == path);
                foreach (var oper in operations)
                {
                    var operationDescription = pathDescriptions.FirstOrDefault(a => a.HttpMethod.Equals(oper.Key.ToString(), StringComparison.InvariantCultureIgnoreCase));
                    foreach (var param in oper.Value.Parameters)
                    {
                        var parameterDescription = operationDescription.ParameterDescriptions.FirstOrDefault(a => a.Name == param.Name);
                        if (parameterDescription != null && TryGetEnumType(parameterDescription.Type, out Type enumType))
                        {
                            var paramEnum = swaggerDoc.Components.Schemas.FirstOrDefault(x => x.Key == enumType.Name);
                            if (paramEnum.Value != null)
                            {
                                param.Description += DescribeEnum(paramEnum.Value.Enum, paramEnum.Key);
                            }
                        }
                    }
                }
            }
        }
    
        bool TryGetEnumType(Type type, out Type enumType)
        {
            if (type.IsEnum)
            {
                enumType = type;
                return true;
            }
            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                var underlyingType = Nullable.GetUnderlyingType(type);
                if (underlyingType != null && underlyingType.IsEnum == true)
                {
                    enumType = underlyingType;
                    return true;
                }
            }
            else
            {
                Type underlyingType = GetTypeIEnumerableType(type);
                if (underlyingType != null && underlyingType.IsEnum)
                {
                    enumType = underlyingType;
                    return true;
                }
                else
                {
                    var interfaces = type.GetInterfaces();
                    foreach (var interfaceType in interfaces)
                    {
                        underlyingType = GetTypeIEnumerableType(interfaceType);
                        if (underlyingType != null && underlyingType.IsEnum)
                        {
                            enumType = underlyingType;
                            return true;
                        }
                    }
                }
            }
    
            enumType = null;
            return false;
        }
    
        Type GetTypeIEnumerableType(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                var underlyingType = type.GetGenericArguments()[0];
                if (underlyingType.IsEnum)
                {
                    return underlyingType;
                }
            }
    
            return null;
        }
    
        private Type GetEnumTypeByName(string enumTypeName)
        {
            return AppDomain.CurrentDomain
                .GetAssemblies()
                .SelectMany(x => x.GetTypes())
                .FirstOrDefault(x => x.Name == enumTypeName);
        }
    
        private string DescribeEnum(IList<IOpenApiAny> enums, string proprtyTypeName)
        {
            List<string> enumDescriptions = new List<string>();
            var enumType = GetEnumTypeByName(proprtyTypeName);
            if (enumType == null)
                return null;
    
            foreach (OpenApiInteger enumOption in enums)
            {
                int enumInt = enumOption.Value;
    
                enumDescriptions.Add(string.Format("{0} = {1}", enumInt, Enum.GetName(enumType, enumInt)));
            }
    
            return string.Join(", ", enumDescriptions.ToArray());
        }
    }
    

    to use the filter add c.DocumentFilter<SwaggerAddEnumDescriptions>(); to swagger configuration in Startup.cs.

    0 讨论(0)
  • 2020-11-27 11:50

    write code inside Startup.cs

    services.AddSwaggerGen(c => {
          c.DescribeAllEnumsAsStrings();
        });
    
    0 讨论(0)
  • 2020-11-27 11:51

    For ASP.NET Core 3 with the Microsoft JSON library (System.Text.Json)

    In Startup.cs/ConfigureServices():

    services
        .AddControllersWithViews(...) // or AddControllers() in a Web API
        .AddJsonOptions(options => 
            options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
    

    For ASP.NET Core 3 with the Json.NET (Newtonsoft.Json) library

    Install the Swashbuckle.AspNetCore.Newtonsoft package.

    In Startup.cs/ConfigureServices():

    services
        .AddControllersWithViews(...)
        .AddNewtonsoftJson(options => 
            options.SerializerSettings.Converters.Add(new StringEnumConverter()));
    // order is vital, this *must* be called *after* AddNewtonsoftJson()
    services.AddSwaggerGenNewtonsoftSupport();
    

    For ASP.NET Core 2

    In Startup.cs/ConfigureServices():

    services
        .AddMvc(...)
        .AddJsonOptions(options => 
            options.SerializerSettings.Converters.Add(new StringEnumConverter()));
    

    Pre-ASP.NET Core

    httpConfiguration
        .EnableSwagger(c => 
            {
                c.DescribeAllEnumsAsStrings();
            });
    
    0 讨论(0)
  • 2020-11-27 11:53

    I have found nice workaround Here:

    @PauloVetor - solved it using ShemaFilter like this:

    public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema model, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                model.Enum.Clear();
                Enum.GetNames(context.Type)
                    .ToList()
                    .ForEach(n => model.Enum.Add(new OpenApiString(n)));
                }
            }
        }
    }
    

    And in Startup.cs:

    services.AddSwaggerGen(options =>
    {
        options.SchemaFilter<EnumSchemaFilter>();
    }
    
    0 讨论(0)
提交回复
热议问题