简体   繁体   English

Swagger UI Web Api 文档 将枚举呈现为字符串?

[英]Swagger UI Web Api documentation Present enums as strings?

Is there a way to display all enums as their string value in swagger instead of their int value?有没有办法将所有枚举显示为 swagger 中的字符串值而不是 int 值?

I want to be able to submit POST actions and put enums according to their string value without having to look at the enum every time.我希望能够提交 POST 操作并根据它们的字符串值放置枚举,而不必每次都查看枚举。

I tried DescribeAllEnumsAsStrings but the server then receives strings instead of the enum value which is not what we're looking for.我尝试了DescribeAllEnumsAsStrings但服务器随后接收到字符串而不是枚举值,这不是我们正在寻找的。

Has anyone solved this?有人解决了吗?

Edit:编辑:

public class Letter 
{
    [Required]
    public string Content {get; set;}

    [Required]
    [EnumDataType(typeof(Priority))]
    public Priority Priority {get; set;}
}


public class LettersController : ApiController
{
    [HttpPost]
    public IHttpActionResult SendLetter(Letter letter)
    {
        // Validation not passing when using DescribeEnumsAsStrings
        if (!ModelState.IsValid)
            return BadRequest("Not valid")

        ..
    }

    // In the documentation for this request I want to see the string values of the enum before submitting: Low, Medium, High. Instead of 0, 1, 2
    [HttpGet]
    public IHttpActionResult GetByPriority (Priority priority)
    {

    }
}


public enum Priority
{
    Low, 
    Medium,
    High
}

Enable globally全局启用

From the docs :文档

httpConfiguration
    .EnableSwagger(c => 
        {
            c.SingleApiVersion("v1", "A title for your API");
            
            c.DescribeAllEnumsAsStrings(); // this will do the trick
        });

Enum/string conversion on particular property特定属性的枚举/字符串转换

Also, if you want this behavior only on a particular type and property, use the StringEnumConverter:此外,如果您只想在特定类型和属性上使用此行为,请使用 StringEnumConverter:

public class Letter 
{
    [Required]
    public string Content {get; set;}

    [Required]
    [EnumDataType(typeof(Priority))]
    [JsonConverter(typeof(StringEnumConverter))]
    public Priority Priority {get; set;}
}

If you're using Newtonsoft and Swashbuckle v5.0.0 or higher如果您使用 Newtonsoft 和 Swashbuckle v5.0.0 或更高版本

You'll also need this package:你还需要这个包:

Swashbuckle.AspNetCore.Newtonsoft

And this in your startup:这在你的启动中:

services.AddSwaggerGenNewtonsoftSupport(); // explicit opt-in - needs to be placed after AddSwaggerGen()

There's docs here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft这里有文档: https ://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft

For ASP.NET Core 3 with the Microsoft JSON library (System.Text.Json)对于带有 Microsoft JSON 库 (System.Text.Json) 的 ASP.NET Core 3

In Startup.cs/ConfigureServices():在 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对于带有 Json.NET (Newtonsoft.Json) 库的 ASP.NET Core 3

Install the Swashbuckle.AspNetCore.Newtonsoft package.安装Swashbuckle.AspNetCore.Newtonsoft包。

In Startup.cs/ConfigureServices():在 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对于 ASP.NET Core 2

In Startup.cs/ConfigureServices():在 Startup.cs/ConfigureServices() 中:

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

Pre-ASP.NET Core预 ASP.NET Core

httpConfiguration
    .EnableSwagger(c => 
        {
            c.DescribeAllEnumsAsStrings();
        });

So I think I have a similar problem.所以我想我也有类似的问题。 I'm looking for swagger to generate enums along with the int -> string mapping.我正在寻找 swagger 来生成枚举以及 int -> 字符串映射。 The API must accept the int. API 必须接受 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). swagger-ui 没那么重要,我真正想要的是代码生成在另一边有一个“真正的”枚举(在这种情况下,android 应用程序使用改造)。

So from my research this ultimately seems to be a limit of the OpenAPI specification which Swagger uses.所以从我的研究来看,这最终似乎是 Swagger 使用的 OpenAPI 规范的一个限制。 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.我发现要遵循的最佳问题是https://github.com/OAI/OpenAPI-Specification/issues/681看起来像“可能很快”,但随后必须更新 Swagger,在我的情况下 Swashbuckle 为出色地。

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: 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":这会在您的 swagger-ui 上产生类似以下内容,因此至少您可以“看到您在做什么”: 在此处输入图像描述

ASP.NET Core 3.1 ASP.NET 核心 3.1

To generate enums as strings using Newtonsoft JSON you must explicitly add Newtonsoft support by adding AddSwaggerGenNewtonsoftSupport() as follows:要使用 Newtonsoft JSON 将枚举生成为字符串,您必须通过添加AddSwaggerGenNewtonsoftSupport()显式添加 Newtonsoft 支持,如下所示:

services.AddMvc()
    ...
    .AddNewtonsoftJson(opts =>
    {
        opts.SerializerSettings.Converters.Add(new StringEnumConverter());
    });


services.AddSwaggerGen(...);
services.AddSwaggerGenNewtonsoftSupport(); //

This is available via a new package, Swashbuckle.AspNetCore.Newtonsoft .这可以通过一个新包Swashbuckle.AspNetCore.Newtonsoft获得。 It looks like everything else works fine without this package apart from enum converter support.看起来除了枚举转换器支持之外,如果没有这个包,其他一切都可以正常工作。

.NET CORE 3.1 and SWAGGER 5 .NET CORE 3.1 和 SWAGGER 5

if you need a simple solution to selectively make enums passed as strings:如果您需要一个简单的解决方案来选择性地将枚举作为字符串传递:

using System.Text.Json.Serialization;


[JsonConverter(typeof(JsonStringEnumConverter))]
public enum MyEnum
{
    A, B
}

Note, we use System.Text.Json.Serialization namespace, not the Newtonsoft.Json !注意,我们使用System.Text.Json.Serialization命名空间,而不是Newtonsoft.Json

I wanted to use rory_za's answer in a .NET Core application, but I had to modify it a bit to make it work.我想在 .NET Core 应用程序中使用 rory_za 的答案,但我必须对其进行一些修改才能使其正常工作。 Here is the implementation I came up with for .NET Core.这是我为 .NET Core 提出的实现。

I also changed it so it doesn't assume the underlying type is int , and use new lines between the values for easier reading.我还对其进行了更改,因此它不假定基础类型是int ,并在值之间使用新行以便于阅读。

/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
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)}";
    }
}

Then add this to your ConfigureServices method in Startup.cs:然后将其添加到 Startup.cs 中的ConfigureServices方法中:

c.DocumentFilter<EnumDocumentFilter>();

if anyone is interested i have modified the code to work with如果有人有兴趣,我已经修改了代码以使用

.NET CORE 3 and Swagger V5 .NET CORE 3Swagger V5

    public 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.Values)
        {
            DescribeEnumParameters(pathItem.Operations, swaggerDoc);
        }
    }

    private void DescribeEnumParameters(IDictionary<OperationType, OpenApiOperation> operations, OpenApiDocument swaggerDoc)
    {
        if (operations != null)
        {
            foreach (var oper in operations)
            {
                foreach (var param in oper.Value.Parameters)
                {
                    var paramEnum = swaggerDoc.Components.Schemas.FirstOrDefault(x => x.Key == param.Name);
                    if (paramEnum.Value != null)
                    {
                        param.Description += DescribeEnum(paramEnum.Value.Enum, paramEnum.Key);
                    }
                }
            }
        }
    }

    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 (IOpenApiAny enumOption in enums)
        {
            if (enumOption is OpenApiString @string)
            {
                string enumString = @string.Value;

                enumDescriptions.Add(string.Format("{0} = {1}", (int)Enum.Parse(enumType, enumString), enumString));
            }
            else if (enumOption is OpenApiInteger integer)
            {
                int enumInt = integer.Value;

                enumDescriptions.Add(string.Format("{0} = {1}", enumInt, Enum.GetName(enumType, enumInt)));
            }
        }

        return string.Join(", ", enumDescriptions.ToArray());
    }
}

My variant for enum stings with values:我的枚举变量带有值:

在此处输入图像描述

Configure Services:配置服务:

services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "web server api", Version = "v1" });
                c.SchemaFilter<EnumSchemaFilter>();
            });

Filter:筛选:

public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema model, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                model.Enum.Clear();
                Enum.GetNames(context.Type)
                    .ToList()
                    .ForEach(name => model.Enum.Add(new OpenApiString($"{Convert.ToInt64(Enum.Parse(context.Type, name))} - {name}")));
            }
        }
    }

With asp.net core 3用 asp.net 核心 3

using System.Text.Json.Serialization;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
         services.AddControllers().AddJsonOptions(options =>
             options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

But it seems that Swashbuckle Version 5.0.0-rc4 is not ready to support that.但似乎 Swashbuckle 版本 5.0.0-rc4 还没有准备好支持它。 So we need to use an option(deprecated) in the Swashbuckle config file until it supports and reflects it like Newtonsoft library.所以我们需要在 Swashbuckle 配置文件中使用一个选项(不推荐使用),直到它像 Newtonsoft 库一样支持和反映它。

public void ConfigureServices(IServiceCollection services)
{ 
      services.AddSwaggerGen(c =>
      {
            c.DescribeAllEnumsAsStrings();

The difference between this answer and other answers is using only the Microsoft JSON library instead of Newtonsoft.此答案与其他答案之间的区别在于仅使用 Microsoft JSON 库而不是 Newtonsoft。

This is not possible with standard OpenAPI.这对于标准 OpenAPI 是不可能的。 Enums are described only with their string values.枚举只用它们的字符串值来描述。

Fortunately you can do it with some non-standard extensions that are utilized by your client generator.幸运的是,您可以使用客户端生成器使用的一些非标准扩展来做到这一点。

NSwag supports x-enumNames NSwag 支持x-enumNames

AutoRest supports x-ms-enum . AutoRest 支持x-ms-enum

Openapi-generator supports x-enum-varnames Openapi-generator 支持x-enum-varnames

Other generators might support one of these extensions or have their own.其他生成器可能支持这些扩展之一或拥有自己的扩展。

To generate x-enumNames for NSwag create the following schema filter:要为 NSwag 生成x-enumNames ,请创建以下模式过滤器:

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            var array = new OpenApiArray();
            array.AddRange(Enum.GetNames(context.Type).Select(n => new OpenApiString(n)));
            // NSwag
            schema.Extensions.Add("x-enumNames", array);
            // Openapi-generator
            schema.Extensions.Add("x-enum-varnames", array);
        }
    }
}

And register it as:并将其注册为:

services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
});

For .NET core 5 it is same as .NET core 3.1 which is to add对于 .NET core 5 ,它与要添加的 .NET core 3.1 相同

   options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());

Example:例子:

services.AddControllers(options =>
{
    options.ReturnHttpNotAcceptable = true;
    var builder = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();
    options.Filters.Add(new AuthorizeFilter(builder.Build()));
 }).AddJsonOptions(options =>
 {
    options.JsonSerializerOptions.IgnoreNullValues = true;
    options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
 });

I have found nice workaround Here:我在这里找到了很好的解决方法:

@PauloVetor - solved it using ShemaFilter like this: @PauloVetor - 使用 ShemaFilter 解决它,如下所示:

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:在 Startup.cs 中:

services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
}

To display the enums as strings in swagger, please configure the JsonStringEnumConverter by adding the following line in ConfigureServices :要在 swagger 中将枚举显示为字符串,请通过在 ConfigureServices 中添加以下行来配置 JsonStringEnumConverter:

services.AddControllers().AddJsonOptions(options =>
            options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

If you want to display the enums as strings and int values, you could try to create an EnumSchemaFilter to change the schema, as below:如果要将枚举显示为字符串和 int 值,可以尝试创建一个 EnumSchemaFilter 来更改架构,如下所示:

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

Configure the SwaggerGen to use the above SchemaFilter :配置 SwaggerGen 以使用上述 SchemaFilter :

services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo
            {
                Version = "v1",
                Title = "ToDo API",
                Description = "A simple example ASP.NET Core Web API",
                TermsOfService = new Uri("https://example.com/terms"),
                Contact = new OpenApiContact
                {
                    Name = "Shayne Boyer",
                    Email = string.Empty,
                    Url = new Uri("https://twitter.com/spboyer"),
                },
                License = new OpenApiLicense
                {
                    Name = "Use under LICX",
                    Url = new Uri("https://example.com/license"),
                }
            });
              
            c.SchemaFilter<EnumSchemaFilter>();
        });

I have modified Hosam Rehani's answer to work with nullable enums and with collection of enums also.我已经修改了 Hosam Rehani 的答案以使用可空枚举和枚举集合。 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.它适用于 .net core 3.x 和 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>();使用过滤器添加c.DocumentFilter<SwaggerAddEnumDescriptions>(); to swagger configuration in Startup.cs .Startup.cs中招摇配置。

Simple Solution.简单的解决方案。 It works for me.这个对我有用。

   using System.Text.Json.Serialization;
    
    
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public enum Priority
    {
        Low, 
        Medium,
        High
    }

ASP.NET Core 6 ASP.NET 核心 6

In your program.cs:在您的 program.cs 中:

builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

Please also note:另请注意:

DescribeAllEnumsAsStrings is obsolete DescribeAllEnumsAsStrings 已过时

I just did this and it works fine!我刚做了这个,效果很好!

Startup.cs启动.cs

services.AddSwaggerGen(c => {
  c.DescribeAllEnumsAsStrings();
});

Model.cs模型.cs

public enum ColumnType {
  DATE = 0
}

swagger.json招摇.json

type: {
  enum: ["DATE"],
  type: "string"
}

I hope this helps you how it helped me!我希望这对您有所帮助!

If you are using newtonsof.json then use this如果您使用的是 newtonsof.json 然后使用这个

using Newtonsoft.Json.Converters;


[JsonConverter(typeof(StringEnumConverter))]
public enum MyEnum
{
    A, B
}

If you are using System.Text.Json.Serialization如果您使用 System.Text.Json.Serialization

using System.Text.Json.Serialization;


[JsonConverter(typeof(JsonStringEnumConverter))]
public enum MyEnum
{
    A, B
}

in .net core 3.1 & swagger 5.0.0 :在 .net 核心 3.1 和 swagger 5.0.0 中:

using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace WebFramework.Swagger
{
    public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                var enumValues = schema.Enum.ToArray();
                var i = 0;
                schema.Enum.Clear();
                foreach (var n in Enum.GetNames(context.Type).ToList())
                {
                    schema.Enum.Add(new OpenApiString(n + $" = {((OpenApiPrimitive<int>)enumValues[i]).Value}"));
                    i++;
                }
            }
        }
    }

}

and in Startup.cs :在 Startup.cs 中:

services.AddSwaggerGen(options =>
            {
                #region  EnumDesc
                options.SchemaFilter<EnumSchemaFilter>();
                #endregion
            });

结果

write code inside Startup.cs在 Startup.cs 中编写代码

services.AddSwaggerGen(c => {
      c.DescribeAllEnumsAsStrings();
    });

There were a number of shortcomings I found in the other answers for what we were looking for, so I thought I'd supply my own take on this.我在我们正在寻找的其他答案中发现了许多缺点,所以我想我会对此提供自己的看法。 We're using ASP.NET Core 3.1 with System.Text.Json, but our approach works irrespective of the JSON serializer used.我们将 ASP.NET Core 3.1 与 System.Text.Json 结合使用,但我们的方法与使用的 JSON 序列化程序无关。

Our goal was to accept lower-camel-cased enum string values in both the ASP.NET Core API as well as document the same in Swagger.我们的目标是在 ASP.NET Core API 以及在 Swagger 中记录相同的文档时,都接受小写的枚举字符串值。 We're currently making use of [DataContract] and [EnumMember] , so the approach is to take the lower-camel-cased value from the EnumMember value property and use that across the board.我们目前正在使用[DataContract][EnumMember] ,因此方法是从 EnumMember 值属性中获取小写字母的值并全面使用它。

Our sample enum:我们的示例枚举:

[DataContract]
public class enum Colors
{
  [EnumMember(Value="brightPink")]
  BrightPink,
  [EnumMember(Value="blue")]
  Blue
}

We'll use the EnumMember values in Swashbuckle by using an ISchemaFilter as in the following:我们将通过使用 ISchemaFilter 在 Swashbuckle 中使用 EnumMember 值,如下所示:

public class DescribeEnumMemberValues : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            schema.Enum.Clear();

            //Retrieve each of the values decorated with an EnumMember attribute
            foreach (var member in context.Type.GetMembers())
            {
                var memberAttr = member.GetCustomAttributes(typeof(EnumMemberAttribute), false).FirstOrDefault();
                if (memberAttr != null)
                {
                    var attr = (EnumMemberAttribute) memberAttr;
                    schema.Enum.Add(new OpenApiString(attr.Value));
                }
            }
        }
    }
}

We're using a third-party NuGet package (GitHub repo ) to ensure that this naming scheme is also utilized in ASP.NET Core.我们正在使用 第三方 NuGet 包(GitHub repo ) 以确保在 ASP.NET Core 中也使用此命名方案。 Configure it in Startup.cs within ConfigureServices with:在 ConfigureServices 的 Startup.cs 中配置它:

services.AddControllers()
  .AddJsonOptions(opt => opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverterWithAttributeSupport()));

Finally, we need to register our ISchemaFilter in Swashbuckle, so also add the following also in ConfigureServices():最后,我们需要在 Swashbuckle 中注册我们的 ISchemaFilter,所以还要在 ConfigureServices() 中添加以下内容:

services.AddSwaggerGen(c => {
  c.SchemaFilter<DescribeEnumMemberValues>();
});

If the version of the swagger were 5.5.x, then you need to:如果 swagger 的版本是 5.5.x,那么你需要:

  1. install: Install-Package Swashbuckle.AspNetCore.Newtonsoft -Version 5.5.0安装:安装包 Swashbuckle.AspNetCore.Newtonsoft -版本 5.5.0

  2. services.AddSwaggerGenNewtonsoftSupport(); services.AddSwaggerGenNewtonsoftSupport();

Reference: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft参考: https ://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft

.Net Core 3.0 .Net Core 3.0

   using Newtonsoft.Json.Converters;

 services
    .AddMvc(options =>
    {
     options.EnableEndpointRouting = false;
     })
    .AddNewtonsoftJson(options => options.SerializerSettings.Converters.Add(new StringEnumConverter()))

ASP NET SOLUTION ASP 网络解决方案

In my api docs one enum was still shown as int despite the property being marked with StringEnumConverter .在我的 api 文档中,一个枚举仍然显示为 int,尽管该属性被标记为StringEnumConverter We couldn't afford using the global setting for all enums mentioned above.我们无法为上述所有枚举使用全局设置。 Adding this line in SwaggerConfig solved the issue:在 SwaggerConfig 中添加这一行解决了这个问题:

c.MapType<ContactInfoType>(() => new Schema { type = "string", @enum = Enum.GetNames(typeof(ContactInfoType))});

In order to generate enums with name and value you can use this solution.为了生成具有名称和值的枚举,您可以使用此解决方案。

Prerequisites:先决条件:

Register SchemaFilter:注册 SchemaFilter:

GlobalConfiguration.Configuration
    .EnableSwagger("/swagger", c =>
    {
        c.SchemaFilter<EnumSchemaFilter>();
    });

EnumSchemaFilter:枚举架构过滤器:

public class EnumSchemaFilter : ISchemaFilter
{
    public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
    {
        var enumProperties = schema.properties?.Where(p => p.Value.@enum != null);
        if(enumProperties != null)
        {
            foreach (var property in enumProperties)
            {
                var array = Enum.GetNames(type.GetProperty(property.Key).PropertyType).ToArray();
                property.Value.vendorExtensions.Add("x-enumNames", array); // NSwag
                property.Value.vendorExtensions.Add("x-enum-varnames", array); // Openapi-generator
            }
        }
    }
}

After searching for answers here, I found the partial solution for the issue with displaying enums in the Schema as [SomeEnumString = 0, AnotherEnumString = 1] , but all of the answers related to this are only partially correct as @OhWelp mentioned in one of the comments.在此处搜索答案后,我找到了在架构中将枚举显示为[SomeEnumString = 0, AnotherEnumString = 1]的问题的部分解决方案,但与此相关的所有答案都只是部分正确,因为@OhWelp在其中之一中提到评论。

Here's the full solution for .NET core (2, 3, currently working on 6):这是 .NET core 的完整解决方案(2、3,目前正在开发 6):

    public class EnumSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema model, SchemaFilterContext context)
        {
            if (context.Type.IsEnum)
            {
                model.Enum.Clear();

                var names = Enum.GetNames(context.Type).ToList();

                names.ForEach(name => model.Enum.Add(new OpenApiString($"{GetEnumIntegerValue(name, context)} = {name}")));


                 // the missing piece that will make sure that the new schema will not replace the mock value with a wrong value 
                // this is the default behavior - the first possible enum value as a default "example" value
                model.Example = new OpenApiInteger(GetEnumIntegerValue(names.First(), context));
            }
        }

        private int GetEnumIntegerValue(string name, SchemaFilterContext context) => Convert.ToInt32(Enum.Parse(context.Type, name));
    }

And in the startup/program:在启动/程序中:

    services.AddSwaggerGen(options =>
        {
            options.SchemaFilter<EnumSchemaFilter>();
        });

EDIT: Refactored the code a bit, if you want to see the original version which is more similar to the rest of the answers please, check the chronology of edits.编辑:对代码进行了一些重构,如果您想查看与其余答案更相似的原始版本,请检查编辑年表。

In.Net 6 with NSwag and System.Text.Json for me works:带有 NSwag 和 System.Text.Json 的 In.Net 6 对我有用:

services.AddControllersWithViews()
       .AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())) 

and

services.AddOpenApiDocument(configure =>
{
       ...
       configure.GenerateEnumMappingDescription = true;
});

It accepts int-s and strings, generates enums in open-api and.ts client with names and show enums with names in SwaggerUI它接受 int-s 和字符串,在 open-api 和 .ts 客户端中生成带有名称的枚举,并在 SwaggerUI 中显示带有名称的枚举

export enum PaymentDirection {
    Input = "Input",
    Output = "Output",
}

Go to Program.cs and put the below code: Go 到 Program.cs 并输入以下代码:

using Microsoft.AspNetCore.Http.Json;
using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions;

.....

builder.Services.Configure<JsonOptions>(o => o.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.Configure<MvcJsonOptions>(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

I got it working for .NET 6 Web API using the following code based from the other answers here:我使用基于此处其他答案的以下代码为 .NET 6 Web API 工作

1 - Create a DocumentFilter 1 - 创建文档过滤器

/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
public class SwaggerEnumDocumentFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        // add enum descriptions to result models
        foreach (var property in swaggerDoc.Components.Schemas)
        {
            var propertyEnums = property.Value.Enum;
            if (propertyEnums is { Count: > 0 })
            {
                property.Value.Description += DescribeEnum(propertyEnums, property.Key);
            }
        }

        if (swaggerDoc.Paths.Count <= 0)
        {
            return;
        }

        // add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths.Values)
        {
            DescribeEnumParameters(pathItem.Parameters);

            var affectedOperations = new List<OperationType> { OperationType.Get, OperationType.Post, OperationType.Put, OperationType.Patch };

            foreach (var operation in pathItem.Operations)
            {
                if (affectedOperations.Contains(operation.Key))
                {
                    DescribeEnumParameters(operation.Value.Parameters);
                }
            }
        }
    }

    private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
    {
        if (parameters == null) return;

        foreach (var param in parameters)
        {
            if (param.Schema.Reference != null)
            {
                var enumType = GetEnumTypeByName(param.Schema.Reference.Id);
                var names = Enum.GetNames(enumType).ToList();

                param.Description += string.Join(", ", names.Select(name => $"{Convert.ToInt32(Enum.Parse(enumType, name))} - {name}").ToList());
            }
        }
    }

    private static Type GetEnumTypeByName(string enumTypeName)
    {
        if (string.IsNullOrEmpty(enumTypeName))
        {
            return null;
        }

        try
        {
            return AppDomain.CurrentDomain
                            .GetAssemblies()
                            .SelectMany(x => x.GetTypes())
                            .Single(x => x.FullName != null
                                      && x.Name == enumTypeName);
        }
        catch (InvalidOperationException e)
        {
            throw new Exception($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
        }
    }

    private static string DescribeEnum(IEnumerable<IOpenApiAny> enums, string propertyTypeName)
    {
        var enumType = GetEnumTypeByName(propertyTypeName);

        if (enumType == null)
        {
            return null;
        }

        var parsedEnums = new List<OpenApiInteger>();
        foreach (var @enum in enums)
        {
            if (@enum is OpenApiInteger enumInt)
            {
                parsedEnums.Add(enumInt);
            }
        }

        return string.Join(", ", parsedEnums.Select(x => $"{x.Value} - {Enum.GetName(enumType, x.Value)}"));
    }

}

2 - Add it to your Program.cs file 2 - 将其添加到您的 Program.cs 文件

   services.AddSwaggerGen(config =>
        {

            config.DocumentFilter<SwaggerEnumDocumentFilter>();

        })

架构结果

范围

I had some problems to get the answers for Swagger and .NET for version 6.x working.我在获得 Swagger 和 .NET 版本 6.x 工作的答案时遇到了一些问题。 I wanted to continue using integer values for enums, but also display a list of possible values (in a readable format).我想继续对枚举使用整数值,但也显示可能值的列表(以可读格式)。 So here is my modified version (includes parts of some answers), maybe it saves some time for some of you ;)所以这是我的修改版本(包括一些答案的一部分),也许它为你们中的一些人节省了一些时间;)

PS There is still some room for improvement, you should also check if the logic of the method "GetEnumTypeByName" fits for you. PS 还有一些改进的空间,你还应该检查“GetEnumTypeByName”方法的逻辑是否适合你。 In my case I wanted to primarily update descriptions only for project internal and unique enums.就我而言,我想主要只更新项目内部和唯一枚举的描述。

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Models.Api;
using Swashbuckle.AspNetCore.SwaggerGen;

/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
public class SwaggerEnumDocumentFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        // add enum descriptions to result models
        foreach (var property in swaggerDoc.Components.Schemas)
        {
            var propertyEnums = property.Value.Enum;
            if (propertyEnums is { Count: > 0 })
            {
                property.Value.Description += DescribeEnum(propertyEnums, property.Key);
            }
        }

        if (swaggerDoc.Paths.Count <= 0)
        {
            return;
        }

        // add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths.Values)
        {
            DescribeEnumParameters(pathItem.Parameters);

            var affectedOperations = new List<OperationType> { OperationType.Get, OperationType.Post, OperationType.Put };

            foreach (var operation in pathItem.Operations)
            {
                if (affectedOperations.Contains(operation.Key))
                {
                    DescribeEnumParameters(operation.Value.Parameters);
                }
            }
        }
    }

    private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
    {
        if (parameters == null) return;

        foreach (var param in parameters)
        {
            if (param.Schema.Reference != null)
            {
                param.Description += DescribeEnum(param.Schema.Reference.Id);
            }
        }
    }

    private static Type GetEnumTypeByName(string enumTypeName)
    {
        if (string.IsNullOrEmpty(enumTypeName))
        {
            return null;
        }

        try
        {
            var projectNamespaceRoot = "MyProject.";

            return AppDomain.CurrentDomain
                            .GetAssemblies()
                            .SelectMany(x => x.GetTypes())
                            .Single(x => x.FullName != null
                                      && x.FullName.StartsWith(projectNamespaceRoot)
                                      && x.Name == enumTypeName);
        }
        catch (InvalidOperationException _)
        {
            throw new ApiException($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
        }
    }

    private static string DescribeEnum(IEnumerable<IOpenApiAny> enums, string propertyTypeName)
    {
        var enumType = GetEnumTypeByName(propertyTypeName);

        if (enumType == null)
        {
            return null;
        }

        var parsedEnums = new List<OpenApiInteger>();
        foreach (var @enum in enums)
        {
            if (@enum is OpenApiInteger enumInt)
            {
                parsedEnums.Add(enumInt);
            }
        }

        return string.Join(", ", parsedEnums.Select(x => $"{x} = {Enum.GetName(enumType, x.Value)}"));
    }
}

As already mentioned by others, you have to register this Filter inside your Swagger setup:正如其他人已经提到的,您必须在 Swagger 设置中注册此过滤器:

services.AddSwaggerGen(options =>
        {
            options.SwaggerDoc("v1", new OpenApiInfo
            {
                // some configuration
            });
              
            options.DocumentFilter<SwaggerEnumDocumentFilter>();
        });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM