Documentación de Swagger UI Web Api ¿Presenta enumeraciones como cadenas?

107

¿Hay alguna manera de mostrar todas las enumeraciones como su valor de cadena en swagger en lugar de su valor int?

Quiero poder enviar acciones POST y poner enumeraciones de acuerdo con su valor de cadena sin tener que mirar la enumeración cada vez.

Lo intenté, DescribeAllEnumsAsStringspero el servidor recibe cadenas en lugar del valor de enumeración, que no es lo que estamos buscando.

¿Alguien ha resuelto esto?

Editar:

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
}

fuente
1
¿Quieres que el esquema describa el valor como una cadena pero luego publique un número entero en el servidor? JSON.net manejará bien ambos valores, entonces, ¿la versión de solo enteros es un requisito definido? No creo que Swagger admita un tipo de enumeración con la cadena y el valor entero.
Hux
1
Su comportamiento esperado no está claro, ¿puede explicar mejor qué desea que muestre la interfaz de usuario de Swagger y qué desea POST / PUT en su API web con ejemplos?
Federico Dipuma
Además, si tengo métodos GET que toman enumeración en la URL, quiero que el esquema lo describa como cadenas en la lista desplegable de valores sugeridos
¿Por qué falla la validación de enteros? El tipo debe ser una enumeración en el modelo y el formateador de medios json manejaría correctamente una cadena o un int. Si actualiza la pregunta con un ejemplo, nos ayudaría a comprender por qué falla la validación.
Hux
4
Si se trata de una enumeración de indicadores, debe ser numérica, a menos que tenga valores de enumeración definidos para cada combinación posible de indicadores. Es una locura que Swagger no muestre AMBOS el nombre y el valor de cada enumeración, y en su lugar muestre el número solo (inútil) o solo los nombres (nuevamente, inútil para las banderas que deben especificarse como números).
Triynko

Respuestas:

188

De los documentos :

httpConfiguration
    .EnableSwagger(c => 
        {
            c.SingleApiVersion("v1", "A title for your API");

            c.DescribeAllEnumsAsStrings(); // this will do the trick
        });

Además, si desea este comportamiento solo en un tipo y propiedad en particular, use StringEnumConverter:

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

    [Required]
    [EnumDataType(typeof(Priority))]
    [JsonConverter(typeof(StringEnumConverter))]
    public Priority Priority {get; set;}
}
Xavero
fuente
5
esto no funciona para mí. [EnumDataType (typeof (Priority))] [JsonConverter (typeof (StringEnumConverter))]
Lineker
@NUEVA HAMPSHIRE. sí, usé newtonsoft.json
Lineker
@Lineker, publique su error como una nueva pregunta, siguiendo esta guía: stackoverflow.com/help/mcve
NH.
¡Gracias! Creo que también podría dejar su comentario en la fuente #thiswilldothetrick
Simon_Weaver
5
DescribeAllEnumsAsStringsTrabajó para propiedades de objetos e incluso parámetros de consulta sobre acciones del controlador. Sin embargo, usar EnumDataTypeAttributey JsonConverter(typeof(StringEnumConverter))no funcionó para mí.
bugged87
89

Para ASP.NET Core 3 con la biblioteca JSON de Microsoft (System.Text.Json)

En Startup.cs / ConfigureServices ():

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

Para ASP.NET Core 3 con la biblioteca Json.NET (Newtonsoft.Json)

Instale el Swashbuckle.AspNetCore.Newtonsoftpaquete.

En Startup.cs / ConfigureServices ():

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

Para ASP.NET Core 2

En Startup.cs / ConfigureServices ():

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

Pre-ASP.NET Core

httpConfiguration
    .EnableSwagger(c => 
        {
            c.DescribeAllEnumsAsStrings();
        });
Lee Richardson
fuente
4
El problema de usar options.SerializerSettings.Converters.Add (new StringEnumConverter ())) es que está cambiando el json para todos sus métodos, no solo para Sawshbuckle.
Guillaume
¿Alguien tiene una solución para Azure Functions v2 y / o v3?
Dan Friedman
@DanFriedman Teniendo en cuenta que Swashbuckle no funciona con Azure Functions en absoluto, no tiene suerte.
Ian Kemp
@IanKemp Hay soporte de terceros con el AzureExtensions.Swashbucklepaquete, pero al igual que @DanFriedman no puedo hacer que la enumeración a cadena funcione como se esperaba
wolfyuk
40

Entonces creo que tengo un problema similar. Estoy buscando swagger para generar enumeraciones junto con el mapeo de cadenas int ->. La API debe aceptar el int. El swagger-ui importa menos, lo que realmente quiero es la generación de código con una enumeración "real" en el otro lado (aplicaciones de Android que usan retrofit en este caso).

Entonces, a partir de mi investigación, esto en última instancia parece ser un límite de la especificación de OpenAPI que usa Swagger. No es posible especificar nombres y números para enumeraciones.

El mejor problema que encontré para seguir es https://github.com/OAI/OpenAPI-Specification/issues/681 que parece un "quizás pronto", pero luego Swagger tendría que actualizarse, y en mi caso Swashbuckle como bien.

Por ahora, mi solución ha sido implementar un filtro de documento que busca enumeraciones y completa la descripción relevante con el contenido de la enumeración.

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

}

Esto da como resultado algo como lo siguiente en su swagger-ui para que al menos pueda "ver lo que está haciendo": ingrese la descripción de la imagen aquí

Rory
fuente
1
+1 Estaba buscando agregar descripciones a las enumeraciones (solo para 'describir enum'), nunca pensé en esto. Ya tengo varios filtros en su lugar, pero estaba buscando algo más 'orgánico', pero no hay soporte. Bueno, entonces, filtra todo el camino :)
NSGaga-most-inactive
¡Gracias! Usé esto en mi proyecto, pero lo modifiqué para que funcione con .NET Core. Agregué mi implementación como respuesta.
Gabriel Luci
27

ASP.NET Core 3.1

Para generar enumeraciones como cadenas usando Newtonsoft JSON, debe agregar explícitamente el soporte de Newtonsoft agregando AddSwaggerGenNewtonsoftSupport()lo siguiente:

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


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

Este servicio está disponible a través de un nuevo paquete, Swashbuckle.AspNetCore.Newtonsoft. Parece que todo lo demás funciona bien sin este paquete, aparte del soporte del convertidor de enumeración.

Roman Starkov
fuente
1
Ayuda a configurar esta convención globalmente, pero si necesita aplicar esto solo a ciertos tipos de enumeraciones, deberá leer detenidamente este problema . TL; DR: No es posible aplicar new StringEnumConverter () solo a la propiedad, pero puede aplicarlo a todo el tipo de enumeración.
A. Tretiakov
1
Supongo que si estamos hablando de errores, tampoco es posible utilizar un convertidor completamente personalizado. Swagger no ejecuta los valores de enumeración a través del convertidor personalizado; simplemente lo reconoce StringEnumConvertercomo un caso especial.
Roman Starkov
22

Quería usar la respuesta de rory_za en una aplicación .NET Core, pero tuve que modificarla un poco para que funcionara. Aquí está la implementación que se me ocurrió para .NET Core.

También lo cambié para que no asuma que el tipo subyacente es int, y uso nuevas líneas entre los valores para facilitar la lectura.

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

Luego agregue esto a su ConfigureServicesmétodo en Startup.cs:

c.DocumentFilter<EnumDocumentFilter>();
Gabriel Luci
fuente
¿Es posible eliminar el Enum: Array [6] que aparece a continuación?
Softlion
4
Gran solución, pero las extensiones DescribeEnumParametersestaban vacías en mi proyecto. Tuve que lanzar el paramto NonBodyParametery verificar la enumeración allí:if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true) { param.Description += DescribeEnum(nbParam.Enum); }
Rabban
En mi proyecto, las extensiones también están vacías, usé la solución @Rabban.
Carlos Beppler
1
@Rabban Actualicé mi código para incluir eso. ¿Puedes verificar que lo puse en el lugar correcto? No tuve este problema. Quizás una versión más nueva cambió las cosas.
Gabriel Luci
@GabrielLuci Confirmado y aprobado;)
Rabban
12

Con asp.net core 3

using System.Text.Json.Serialization;

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

Pero parece que Swashbuckle Version 5.0.0-rc4 no está lista para admitir eso. Por lo tanto, debemos usar una opción (en desuso) en el archivo de configuración Swashbuckle hasta que lo admita y refleje como la biblioteca Newtonsoft.

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

La diferencia entre esta respuesta y otras respuestas es usar solo la biblioteca JSON de Microsoft en lugar de Newtonsoft.

Bashir Momen
fuente
Hola @Bashir, ¿hay un problema de swachbuckle para realizar un seguimiento de la falta de ese apoyo?
Bernard Vander Beken
Hola @ bernard-vander-beken, no lo informé, pero supongo que sí. Es bueno si podemos encontrarlo y agregarlo a esta publicación para actualizaciones posteriores.
Bashir Momen
10

.NET CORE 3.1 y SWAGGER 5

si necesita una solución simple para hacer selectivamente enumeraciones pasadas como cadenas:

using System.Text.Json.Serialization;


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

Tenga en cuenta que usamos el System.Text.Json.Serializationespacio de nombres, no el Newtonsoft.Json!

VeganHunter
fuente
Este funciona mostrando los valores adecuados y también funciona al convertir los valores de nuevo a la enumeración. Tenga en cuenta que debe agregar el paquete NuGet System.Text.Json.
MovGP0
¡Eso es lo que estaba buscando! Como tengo que usar una cadena para una sola enumeración, y DescribeAllEnumsAsStringsconvertiré todas las enumeraciones en la cadena.
Nilay
9

si alguien está interesado, he modificado el código para trabajar

.NET CORE 3 y Swagger 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 (OpenApiInteger enumOption in enums)
        {
            int enumInt = enumOption.Value;

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

        return string.Join(", ", enumDescriptions.ToArray());
    }
}
Hosam Rehani
fuente
1
Esto solo funciona cuando el tipo de parámetro es exactamente enum ... no enum anulable, colección de enums, etc. Verifique mi respuesta para esos casos.
Matyas
4

¡Acabo de hacer esto y funciona bien!

Startup.cs

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

Model.cs

public enum ColumnType {
  DATE = 0
}

swagger.json

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

¡Espero que esto te ayude en lo que me ayudó a mí!

Rodrigo Béco
fuente
2
DescribeAllEnumsAsStringsestá obsoleto
Node.JS
4

en .net core 3.1 y 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++;
                }
            }
        }
    }

}

y en Startup.cs:

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

Resultado

ehsan rezaee
fuente
4
El lado negativo de esto es que al ejecutar una solicitud, en lugar de pasar solo la representación int (como 2, por ejemplo) de un valor de enumeración, la API obtendrá la descripción completa como un valor (como LogicError = 3), que fallará como solicitud incorrecta ya que no es un valor válido para la enumeración.
Matyas
3

Mi variante para picaduras de enumeración con valores:

ingrese la descripción de la imagen aquí

Configurar servicios:

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

Filtrar:

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}")));
            }
        }
    }
Andrew Zaitsev
fuente
2

escribir código dentro de Startup.cs

services.AddSwaggerGen(c => {
      c.DescribeAllEnumsAsStrings();
    });
ANJYR - KODEXPRESSION
fuente
2
Esta opción está obsoleta en Swashbuckle. Se recomienda usar la opción ASP.NET Core y luego Swashbuckle puede reflejar eso.
Bashir Momen
2

He encontrado una buena solución aquí:

@PauloVetor - lo resolvió usando ShemaFilter así:

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

Y en Startup.cs:

services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
}
nebulosa-hélice
fuente
También debe asegurarse de actualizar model.Formata "string"como será generalmente "int32".
lsuarez
1

.Net Core 3.0

   using Newtonsoft.Json.Converters;

 services
    .AddMvc(options =>
    {
     options.EnableEndpointRouting = false;
     })
    .AddNewtonsoftJson(options => options.SerializerSettings.Converters.Add(new StringEnumConverter()))
anıl yıldırım
fuente
1
Está utilizando Newtonsoft en lugar de la nueva serialización JSON del núcleo de asp.net.
Bashir Momen
1

He modificado la respuesta de Hosam Rehani para que funcione con enumeraciones que aceptan valores NULL y también con una colección de enumeraciones. La respuesta anterior también funciona solo si una propiedad se nombra exactamente como su tipo. Todos estos problemas se tratan en el código siguiente.

Funciona con .net core 3.xy swagger 5.x.

podría ser más eficiente si no buscara el tipo enum dos veces en algunos casos.

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

para usar el filtro agregar c.DocumentFilter<SwaggerAddEnumDescriptions>();a la configuración swagger en Startup.cs.

Matyas
fuente
0

SOLUCIÓN ASP NET

En mis documentos de API, una enumeración todavía se mostraba como int a pesar de que la propiedad estaba marcada con StringEnumConverter. No podíamos permitirnos usar la configuración global para todas las enumeraciones mencionadas anteriormente. Agregar esta línea en SwaggerConfig resolvió el problema:

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

Hubo una serie de deficiencias que encontré en las otras respuestas para lo que estábamos buscando, así que pensé en dar mi propia opinión sobre esto. Estamos usando ASP.NET Core 3.1 con System.Text.Json, pero nuestro enfoque funciona independientemente del serializador JSON utilizado.

Nuestro objetivo era aceptar valores de cadena de enumeración en mayúsculas y minúsculas en la API de ASP.NET Core y documentar lo mismo en Swagger. Actualmente estamos haciendo uso de [DataContract]y [EnumMember], por lo que el enfoque es tomar el valor menor en caja de camello de la propiedad de valor EnumMember y usarlo en todos los ámbitos.

Nuestra enumeración de muestra:

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

Usaremos los valores de EnumMember en Swashbuckle usando un ISchemaFilter como se muestra a continuación:

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

Estamos usando un paquete NuGet de terceros ( repositorio de GitHub ) para garantizar que este esquema de nomenclatura también se utilice en ASP.NET Core. Configúrelo en Startup.cs dentro de ConfigureServices con:

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

Finalmente, necesitamos registrar nuestro ISchemaFilter en Swashbuckle, así que también agregue lo siguiente también en ConfigureServices ():

services.AddSwaggerGen(c => {
  c.SchemaFilter<DescribeEnumMemberValues>();
});
Xaniff
fuente
GetMembers()sería mejor GetMembers(BindingFlags.Static | BindingFlags.Public)limitar solo a las propiedades de enumeración declaradas reales, como "Azul". También adapté el caso "else" para devolver el Member.Name si no hay ningún [EnumMember]atributo.
user2864740
0

Esto no es posible con OpenAPI estándar. Las enumeraciones se describen solo con sus valores de cadena.

Afortunadamente, puede hacerlo con algunas extensiones no estándar que utiliza su generador de clientes.

Soportes NSwag x-enumNames

Soportes AutoRest x-ms-enum.

Soportes del generador Openapi x-enum-varnames

Otros generadores pueden admitir una de estas extensiones o tener la suya propia.

Para generar x-enumNamespara NSwag, cree el siguiente filtro de esquema:

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

Y regístrelo como:

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