Documentazione API Web dell'interfaccia utente Swagger Presentare enumerazioni come stringhe?


107

C'è un modo per visualizzare tutte le enumerazioni come il loro valore di stringa in swagger invece del loro valore int?

Voglio essere in grado di inviare azioni POST e inserire enumerazioni in base al loro valore di stringa senza dover guardare ogni volta l'enumerazione.

Ho provato DescribeAllEnumsAsStringsma il server riceve le stringhe invece del valore enum che non è quello che stiamo cercando.

Qualcuno ha risolto questo problema?

Modificare:

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
}

1
Vuoi che lo schema descriva il valore come una stringa ma poi invii un numero intero al server? JSON.net gestirà bene entrambi i valori, quindi la versione solo intera è un requisito definito? Non credo che Swagger supporti un tipo enum sia con la stringa che con il valore intero.
Hux

1
Il tuo comportamento previsto non è chiaro, puoi spiegare meglio cosa vuoi che l'interfaccia utente di Swagger mostri e cosa vuoi POST / PUT alla tua API Web con esempi?
Federico Dipuma

Inoltre, se ho metodi GET che accettano enum nell'URL, voglio che lo schema lo descriva come stringhe nell'elenco a discesa dei valori suggeriti

Perché la convalida dei numeri interi non riesce? Il tipo dovrebbe essere un'enumerazione nel modello e il formattatore multimediale json gestirà correttamente una stringa o un int. Se aggiorni la domanda con un esempio, ci aiuterebbe a capire perché la convalida non riesce.
Hux

4
Se è un flag enum, deve essere numerico, a meno che tu non abbia valori enum definiti per ogni possibile combinazione di flag. È assurdo che spavalderia non visualizzi SIA il nome e il valore per ogni enumerazione, e invece visualizzi solo il numero (inutile) o solo i nomi (di nuovo, inutile per i flag che devono essere specificati come numeri).
Triynko

Risposte:


188

Dai documenti :

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

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

Inoltre, se desideri questo comportamento solo su un tipo e una proprietà particolari, utilizza StringEnumConverter:

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

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

5
questo non funziona per me. [EnumDataType (typeof (Priority))] [JsonConverter (typeof (StringEnumConverter))]
Lineker

@NH. sì, ho usato newtonsoft.json
Lineker

@ Lineker, pubblica il tuo errore come nuova domanda, seguendo questa guida: stackoverflow.com/help/mcve
NH.

Grazie! Penso che potrei lasciare il tuo commento anche nella fonte #thiswilldothetrick
Simon_Weaver

5
DescribeAllEnumsAsStringsha funzionato per le proprietà degli oggetti e persino per i parametri di query sulle azioni del controller. Tuttavia, l'utilizzo di EnumDataTypeAttributee JsonConverter(typeof(StringEnumConverter))non ha funzionato per me.
Bugged87

92

Per ASP.NET Core 3 con la libreria Microsoft JSON (System.Text.Json)

In Startup.cs / ConfigureServices ():

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

Per ASP.NET Core 3 con la libreria Json.NET (Newtonsoft.Json)

Installa il Swashbuckle.AspNetCore.Newtonsoftpacchetto.

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

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

4
Il problema dell'utilizzo di options.SerializerSettings.Converters.Add (new StringEnumConverter ())) è che stai cambiando il json per tutti i tuoi metodi, non solo per Sawshbuckle.
Guillaume

Qualcuno ha una soluzione per Funzioni di Azure v2 e / o v3?
Dan Friedman,

@DanFriedman Considerando che Swashbuckle non funziona affatto con le funzioni di Azure, sei sfortunato.
Ian Kemp

@IanKemp C'è il supporto di terze parti con il AzureExtensions.Swashbucklepacchetto, ma come @DanFriedman non riesco a far funzionare l'enumerazione alla stringa come previsto
wolfyuk

40

Quindi penso di avere un problema simile. Sto cercando spavalderia per generare enumerazioni insieme alla mappatura int -> string. L'API deve accettare l'int. La spavalderia-ui conta meno, quello che voglio veramente è la generazione di codice con un enum "reale" sull'altro lato (app Android che utilizzano retrofit in questo caso).

Quindi, dalla mia ricerca, questo alla fine sembra essere un limite della specifica OpenAPI che Swagger usa. Non è possibile specificare nomi e numeri per le enumerazioni.

Il miglior problema che ho trovato da seguire è https://github.com/OAI/OpenAPI-Specification/issues/681 che sembra un "forse presto" ma poi Swagger dovrebbe essere aggiornato, e nel mio caso Swashbuckle come bene.

Per ora la mia soluzione alternativa è stata quella di implementare un filtro del documento che cerca le enumerazioni e popola la descrizione pertinente con il contenuto dell'enumerazione.

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

}

Ciò si traduce in qualcosa di simile al seguente nella tua interfaccia utente spavalda così almeno puoi "vedere cosa stai facendo": inserisci qui la descrizione dell'immagine


1
+1 Stavo cercando di aggiungere descrizioni alle enumerazioni (solo per "descrivere l'enumerazione"), non ci ho mai pensato. Ho già installato vari filtri, ma stavo cercando qualcosa di più "organico", ma non c'è supporto. Ebbene, filtra fino in fondo :)
NSGaga-per lo più inattivo

Grazie! L'ho usato nel mio progetto, ma l'ho modificato per funzionare con .NET Core. Ho aggiunto la mia implementazione come risposta.
Gabriel Luci

27

ASP.NET Core 3.1

Per generare enumerazioni come stringhe utilizzando Newtonsoft JSON è necessario aggiungere esplicitamente il supporto di Newtonsoft aggiungendo AddSwaggerGenNewtonsoftSupport()quanto segue:

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


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

Questo è disponibile tramite un nuovo pacchetto, Swashbuckle.AspNetCore.Newtonsoft. Sembra che tutto il resto funzioni bene senza questo pacchetto a parte il supporto del convertitore enum.


1
È utile impostare questa convenzione a livello globale, ma se è necessario applicarla solo a determinati tipi di enumerazioni, sarà necessario leggere attentamente questo problema . TL; DR: non è possibile applicare new StringEnumConverter () solo alla proprietà, ma è possibile applicarlo all'intero tipo di enumerazione.
A. Tretiakov

1
Suppongo che se stiamo parlando di trucchi, non è nemmeno possibile utilizzare un convertitore completamente personalizzato. Swagger non esegue i valori enum tramite il convertitore personalizzato; riconosce semplicemente StringEnumConvertercome un caso speciale.
Roman Starkov

22

Volevo usare la risposta di rory_za in un'applicazione .NET Core, ma ho dovuto modificarla un po 'per farla funzionare. Ecco l'implementazione che ho creato per .NET Core.

L'ho anche cambiato in modo che non presumesse che il tipo sottostante sia int, e uso nuove linee tra i valori per una lettura più facile.

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

Quindi aggiungi questo al tuo ConfigureServicesmetodo in Startup.cs:

c.DocumentFilter<EnumDocumentFilter>();

È possibile rimuovere Enum: Array [6] che appare sotto?
Softlion

4
Grande soluzione, ma le estensioni in DescribeEnumParameterserano vuote nel mio progetto. Ho dovuto lanciare il paramto NonBodyParametere controllare l'enum lì:if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true) { param.Description += DescribeEnum(nbParam.Enum); }
Rabban il

Sul mio progetto anche le estensioni sono vuote, ho usato la soluzione @Rabban.
Carlos Beppler

1
@ Raban ho aggiornato il mio codice per includerlo. Puoi solo verificare che l'ho messo nel posto giusto? Non ho avuto questo problema. Forse una versione più recente ha cambiato le cose.
Gabriel Luci

@GabrielLuci Confermato e approvato;)
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()));

Ma sembra che Swashbuckle versione 5.0.0-rc4 non sia pronta a supportarlo. Quindi dobbiamo usare un'opzione (deprecata) nel file di configurazione Swashbuckle fino a quando non la supporta e la riflette come la libreria Newtonsoft.

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

La differenza tra questa risposta e altre risposte sta utilizzando solo la libreria Microsoft JSON invece di Newtonsoft.


Ehi @Bashir, c'è un problema con la fibbia per tenere traccia della mancanza di quel supporto?
Bernard Vander Beken

Ciao @ bernard-vander-beken, non l'ho segnalato ma presumo che ci sia. Va bene se riusciamo a trovarlo e aggiungerlo a questo post per aggiornamenti successivi.
Bashir Momen,


10

.NET CORE 3.1 e SWAGGER 5

se hai bisogno di una soluzione semplice per far passare selettivamente le enumerazioni come stringhe:

using System.Text.Json.Serialization;


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

Nota, usiamo lo System.Text.Json.Serializationspazio dei nomi, non il Newtonsoft.Json!


Questo funziona mostrando i valori corretti e funziona anche quando converte i valori di nuovo nell'enum. Tieni presente che devi aggiungere il pacchetto NuGet System.Text.Json.
MovGP0

Questo è quello che stavo cercando! Dato che devo usare la stringa solo per una singola DescribeAllEnumsAsStringsenumerazione e convertirò tutte le enumerazioni nella stringa.
Nilay

9

se qualcuno è interessato ho modificato il codice per lavorarci

.NET CORE 3 e 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());
    }
}

1
Funziona solo quando il tipo di parametro è esattamente enum ... non annullabile enum, raccolta di enumerazioni ecc. Controlla la mia risposta per questi casi.
Matyas

4

L'ho appena fatto e funziona bene!

Startup.cs

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

Model.cs

public enum ColumnType {
  DATE = 0
}

swagger.json

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

Spero che questo ti aiuti come ha aiutato me!


2
DescribeAllEnumsAsStringsè deprecato
Node.JS

4

in .net core 3.1 e 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++;
                }
            }
        }
    }

}

e in Startup.cs:

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

Risultato


4
Il lato negativo di questo è che quando si esegue una richiesta, invece di passare solo la rappresentazione int (come 2 per esempio) di un valore enum, l'API otterrà la descrizione completa come valore (come LogicError = 3), che fallirà come un richiesta errata poiché non è un valore valido per l'enumerazione.
Matyas

3

La mia variante per enum stings con valori:

inserisci qui la descrizione dell'immagine

Configura servizi:

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

Filtro:

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

2

scrivi il codice all'interno di Startup.cs

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

2
Questa opzione è deprecata in Swashbuckle. Si consiglia di utilizzare l'opzione ASP.NET Core e quindi Swashbuckle può rifletterlo.
Bashir Momen

2

Ho trovato una bella soluzione qui:

@PauloVetor - risolto usando ShemaFilter in questo modo:

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

E in Startup.cs:

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

Dovresti anche assicurarti di aggiornare il model.Formata "string"come sarà 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()))

1
Utilizza Newtonsoft invece della nuova serializzazione JSON core asp.net.
Bashir Momen

1

Ho modificato la risposta di Hosam Rehani per lavorare con enumerazioni nullable e anche con la raccolta di enumerazioni. La risposta precedente funziona anche solo se una proprietà è denominata esattamente come il suo tipo. Tutti questi problemi vengono risolti nel codice seguente.

Funziona con .net core 3.xe swagger 5.x.

in alcuni casi potrebbe essere più efficiente non cercare due volte il tipo enum.

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

per utilizzare il filtro aggiungi c.DocumentFilter<SwaggerAddEnumDescriptions>();alla configurazione di swagger in Startup.cs.


0

SOLUZIONE ASP NET

Nei miei documenti api un enum era ancora mostrato come int nonostante la proprietà fosse contrassegnata con StringEnumConverter. Non potevamo permetterci di utilizzare l'impostazione globale per tutte le enumerazioni sopra menzionate. L'aggiunta di questa riga in SwaggerConfig ha risolto il problema:

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

0

C'erano una serie di carenze che ho trovato nelle altre risposte per quello che stavamo cercando, quindi ho pensato di fornire la mia opinione su questo. Stiamo usando ASP.NET Core 3.1 con System.Text.Json, ma il nostro approccio funziona indipendentemente dal serializzatore JSON utilizzato.

Il nostro obiettivo era accettare valori di stringa enum con lettere maiuscole e minuscole nell'API ASP.NET Core e documentare lo stesso in Swagger. Attualmente stiamo facendo uso di [DataContract]e [EnumMember], quindi l'approccio è prendere il valore inferiore del cammello dalla proprietà del valore EnumMember e usarlo su tutta la linea.

Il nostro esempio enum:

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

Useremo i valori EnumMember in Swashbuckle utilizzando un ISchemaFilter come nel seguente:

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

Stiamo usando un pacchetto NuGet di terze parti ( repository GitHub ) per garantire che questo schema di denominazione venga utilizzato anche in ASP.NET Core. Configuralo in Startup.cs all'interno di ConfigureServices con:

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

Infine, dobbiamo registrare il nostro ISchemaFilter in Swashbuckle, quindi aggiungere anche quanto segue anche in ConfigureServices ():

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

GetMembers()sarebbe meglio GetMembers(BindingFlags.Static | BindingFlags.Public)limitare alle sole proprietà enum dichiarate effettive come "Blue". Ho anche adattato il caso "else" per restituire Member.Name se non ci sono [EnumMember]attributi.
user2864740

0

Questo non è possibile con OpenAPI standard. Gli enum vengono descritti solo con i loro valori di stringa.

Fortunatamente puoi farlo con alcune estensioni non standard che vengono utilizzate dal tuo generatore di client.

NSwag supporta x-enumNames

Supporta AutoRest x-ms-enum.

Openapi-generator supporta x-enum-varnames

Altri generatori potrebbero supportare una di queste estensioni o averne una propria.

Per generare x-enumNamesper NSwag, creare il seguente filtro dello schema:

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

E registralo come:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.