Documentation Swagger UI Web Api Présenter les énumérations sous forme de chaînes?

107

Existe-t-il un moyen d'afficher toutes les énumérations en tant que valeur de chaîne dans swagger au lieu de leur valeur int?

Je veux pouvoir soumettre des actions POST et mettre des énumérations en fonction de leur valeur de chaîne sans avoir à regarder l'énumération à chaque fois.

J'ai essayé DescribeAllEnumsAsStringsmais le serveur reçoit alors des chaînes au lieu de la valeur enum qui n'est pas ce que nous recherchons.

Quelqu'un a-t-il résolu cela?

Éditer:

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
}

la source
1
Voulez-vous que le schéma décrive la valeur sous forme de chaîne, puis publie un entier sur le serveur? JSON.net gérera bien les deux valeurs, la version entière uniquement est-elle une exigence définitive? Je ne pense pas que Swagger prend en charge un type enum avec à la fois la chaîne et la valeur entière.
Hux
1
Votre comportement attendu n'est pas clair, pouvez-vous mieux expliquer ce que vous voulez que l'interface utilisateur Swagger affiche et ce que vous souhaitez POST / PUT sur votre API Web avec des exemples?
Federico Dipuma
De plus, si j'ai des méthodes GET qui prennent enum dans l'url, je veux que le schéma le décrive comme des chaînes dans la liste déroulante des valeurs suggérées
Pourquoi la validation d'entiers échoue-t-elle? Le type doit être une énumération dans le modèle et le formateur de média json gérerait correctement une chaîne ou un int. Si vous mettez à jour la question avec un exemple, cela nous aiderait à comprendre pourquoi la validation échoue.
Hux
4
S'il s'agit d'une énumération d'indicateurs, elle doit être numérique, sauf si vous avez défini des valeurs d'énumération pour chaque combinaison possible d'indicateurs. C'est fou que swagger n'affiche pas À LA FOIS le nom et la valeur de chaque énumération, et affiche à la place le nombre seul (inutile) ou les noms seuls (encore une fois, inutile pour les indicateurs qui doivent être spécifiés sous forme de nombres).
Triynko

Réponses:

188

À partir de la documentation :

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

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

De plus, si vous souhaitez ce comportement uniquement sur un type et une propriété particuliers, utilisez StringEnumConverter:

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

    [Required]
    [EnumDataType(typeof(Priority))]
    [JsonConverter(typeof(StringEnumConverter))]
    public Priority Priority {get; set;}
}
Xavero
la source
5
cela ne fonctionne pas pour moi. [EnumDataType (typeof (Priority))] [JsonConverter (typeof (StringEnumConverter))]
Lineker
@NH. oui, j'ai utilisé newtonsoft.json
Lineker
@Lineker, postez votre erreur sous forme de nouvelle question, en suivant ce guide: stackoverflow.com/help/mcve
NH.
THX! Je pense que je pourrais aussi laisser votre commentaire dans la source #thiswilldothetrick
Simon_Weaver
5
DescribeAllEnumsAsStringstravaillé pour les propriétés des objets et même les paramètres de requête sur les actions du contrôleur. Cependant, utiliser EnumDataTypeAttributeet JsonConverter(typeof(StringEnumConverter))n'a pas fonctionné pour moi.
buggé87
92

Pour ASP.NET Core 3 avec la bibliothèque Microsoft JSON (System.Text.Json)

Dans Startup.cs / ConfigureServices ():

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

Pour ASP.NET Core 3 avec la bibliothèque Json.NET (Newtonsoft.Json)

Installez le Swashbuckle.AspNetCore.Newtonsoftpackage.

Dans Startup.cs / ConfigureServices ():

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

Pour ASP.NET Core 2

Dans Startup.cs / ConfigureServices ():

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

Pre-ASP.NET Core

httpConfiguration
    .EnableSwagger(c => 
        {
            c.DescribeAllEnumsAsStrings();
        });
Lee Richardson
la source
4
Le problème de l'utilisation d'options.SerializerSettings.Converters.Add (new StringEnumConverter ())) est que vous changez le json pour toutes vos méthodes, pas seulement pour Sawshbuckle.
Guillaume
Quelqu'un a-t-il une solution pour Azure Functions v2 et / ou v3?
Dan Friedman le
@DanFriedman Considérant que Swashbuckle ne fonctionne pas du tout avec Azure Functions, vous n'avez pas de chance.
Ian Kemp
@IanKemp Il existe un support tiers avec le AzureExtensions.Swashbucklepackage, mais comme @DanFriedman, je ne peux pas faire fonctionner l'énumération à la chaîne comme prévu
wolfyuk
40

Je pense donc que j'ai un problème similaire. Je recherche swagger pour générer des énumérations avec le mappage int -> string. L'API doit accepter l'int. Le swagger-ui importe moins, ce que je veux vraiment, c'est la génération de code avec une "vraie" énumération de l'autre côté (applications Android utilisant la modernisation dans ce cas).

Donc, d'après mes recherches, cela semble finalement être une limite de la spécification OpenAPI que Swagger utilise. Il n'est pas possible de spécifier des noms et des numéros pour les énumérations.

Le meilleur problème que j'ai trouvé à suivre est https://github.com/OAI/OpenAPI-Specification/issues/681 qui ressemble à un "peut-être bientôt" mais alors Swagger devrait être mis à jour, et dans mon cas Swashbuckle comme bien.

Pour l'instant, ma solution de contournement a été d'implémenter un filtre de document qui recherche les énumérations et remplit la description appropriée avec le contenu de l'énumération.

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

}

Il en résulte quelque chose comme ce qui suit sur votre swagger-ui afin qu'au moins vous puissiez "voir ce que vous faites": entrez la description de l'image ici

Rory
la source
1
+1 Je cherchais à ajouter des descriptions aux énumérations (juste pour «décrire enum»), je n'y ai jamais pensé. J'ai déjà mis en place divers filtres, mais je cherchais quelque chose de plus «organique», mais il n'y a pas de support. Eh bien,
filtrez complètement
Merci! J'ai utilisé ceci dans mon projet, mais je l'ai modifié pour qu'il fonctionne avec .NET Core. J'ai ajouté ma mise en œuvre comme réponse.
Gabriel Luci
27

ASP.NET Core 3.1

Pour générer des énumérations sous forme de chaînes à l'aide de Newtonsoft JSON, vous devez explicitement ajouter la prise en charge de Newtonsoft en ajoutant AddSwaggerGenNewtonsoftSupport()comme suit:

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


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

Il est disponible via un nouveau package, Swashbuckle.AspNetCore.Newtonsoft. Il semble que tout le reste fonctionne correctement sans ce package, à l'exception de la prise en charge du convertisseur enum.

Roman Starkov
la source
1
Cela aide à configurer cette convention globalement, mais si vous devez l'appliquer uniquement à certains types d'énumérations, vous devrez lire attentivement ce problème . TL; DR: Il n'est pas possible d'appliquer le nouveau StringEnumConverter () à la propriété uniquement, mais vous pouvez l'appliquer à tout le type enum.
A. Tretiakov le
1
Je suppose que si nous parlons de pièges, il n'est pas non plus possible d'utiliser un convertisseur complètement personnalisé. Swagger n'exécute pas les valeurs d'énumération via le convertisseur personnalisé; il reconnaît simplement StringEnumConvertercomme un cas particulier.
Roman Starkov le
22

Je voulais utiliser la réponse de rory_za dans une application .NET Core, mais j'ai dû la modifier un peu pour qu'elle fonctionne. Voici l'implémentation que j'ai proposée pour .NET Core.

Je l'ai également changé pour ne pas supposer que le type sous-jacent est intet utiliser de nouvelles lignes entre les valeurs pour une lecture plus 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)}";
    }
}

Ajoutez ensuite ceci à votre ConfigureServicesméthode dans Startup.cs:

c.DocumentFilter<EnumDocumentFilter>();
Gabriel Luci
la source
Possible de supprimer le Enum: Array [6] qui apparaît ci-dessous?
Softlion
4
Excellente solution, mais les extensions de DescribeEnumParametersétaient vides dans mon projet. J'ai dû lancer le paramto NonBodyParameteret vérifier l'énumération là-bas:if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true) { param.Description += DescribeEnum(nbParam.Enum); }
Rabban
Sur mon projet, les extensions sont également vides, ont utilisé la solution @Rabban.
Carlos Beppler
1
@Rabban J'ai mis à jour mon code pour l'inclure. Pouvez-vous simplement vérifier que je l'ai mis au bon endroit? Je n'ai pas eu ce problème. Peut-être qu'une version plus récente a changé les choses.
Gabriel Luci
@GabrielLuci Confirmé et approuvé;)
Rabban
12

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

Mais il semble que Swashbuckle version 5.0.0-rc4 ne soit pas prêt à prendre en charge cela. Nous devons donc utiliser une option (obsolète) dans le fichier de configuration Swashbuckle jusqu'à ce qu'elle le prenne en charge et le reflète comme la bibliothèque Newtonsoft.

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

La différence entre cette réponse et d'autres réponses consiste à utiliser uniquement la bibliothèque Microsoft JSON au lieu de Newtonsoft.

Bashir Momen
la source
Hé @Bashir, y a-t-il un problème de swachbuckle pour suivre le manque de soutien?
Bernard Vander Beken
Salut @ bernard-vander-beken, je n'ai pas signalé cela mais je suppose que oui. C'est bien si nous pouvons le trouver et l'ajouter à cet article pour une mise à jour ultérieure.
Bashir Momen
10

.NET CORE 3.1 et SWAGGER 5

si vous avez besoin d'une solution simple pour rendre sélectivement les énumérations passées sous forme de chaînes:

using System.Text.Json.Serialization;


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

Notez que nous utilisons l' System.Text.Json.Serializationespace de noms, pas le Newtonsoft.Json!

VeganHunter
la source
Celui-ci fonctionne en affichant les valeurs appropriées et fonctionne également lors de la conversion des valeurs en énumération. Notez que vous devez ajouter le package NuGet System.Text.Json.
MovGP0
C'est ce que je cherchais! Comme je dois utiliser une chaîne pour une seule énumération, et DescribeAllEnumsAsStringsje vais convertir toutes les énumérations en chaîne.
Nilay
9

si quelqu'un est intéressé, j'ai modifié le code pour travailler avec

.NET CORE 3 et 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
la source
1
Cela ne fonctionne que lorsque le type de paramètre est exactement enum ... non nullable enum, collection d'énumérations, etc. Vérifiez ma réponse pour ces cas.
Matyas le
4

Je viens de faire ça et ça marche bien!

Startup.cs

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

Model.cs

public enum ColumnType {
  DATE = 0
}

swagger.json

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

J'espère que cela vous aidera comment cela m'a aidé!

Rodrigo Béco
la source
2
DescribeAllEnumsAsStringsest obsolète
Node.JS
4

dans .net core 3.1 et 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++;
                }
            }
        }
    }

}

et dans Startup.cs:

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

Résultat

ehsan rezaee
la source
4
L'inconvénient est que lors de l'exécution d'une requête, au lieu de ne transmettre qu'une représentation int (comme 2 par exemple) d'une valeur enum, l'API obtiendra la description complète sous forme de valeur (comme LogicError = 3), qui échouera en tant que mauvaise requête car ce n'est pas une valeur valide pour l'énumération.
Matyas le
3

Ma variante pour les enum stings avec des valeurs:

entrez la description de l'image ici

Configurer les services:

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

Filtre:

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
la source
2

écrire du code dans Startup.cs

services.AddSwaggerGen(c => {
      c.DescribeAllEnumsAsStrings();
    });
ANJYR - KODEXPRESSION
la source
2
Cette option est obsolète dans le Swashbuckle. Il est recommandé d'utiliser l'option ASP.NET Core, puis Swashbuckle peut refléter cela.
Bashir Momen
2

J'ai trouvé une bonne solution de contournement ici:

@PauloVetor - l'a résolu en utilisant ShemaFilter comme ceci:

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

Et dans Startup.cs:

services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
}
hélice-nébuleuse
la source
Vous devez également vous assurer de mettre model.Formatà jour le vers "string"tel qu'il sera généralement "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
la source
1
Il utilise Newtonsoft au lieu de la nouvelle sérialisation JSON de base asp.net.
Bashir Momen
1

J'ai modifié la réponse de Hosam Rehani pour qu'elle fonctionne avec des énumérations nullables et avec une collection d'énumérations également. La réponse précédente ne fonctionne également que si une propriété est nommée exactement comme son type. Tous ces problèmes sont traités dans le code ci-dessous.

Il fonctionne avec .net core 3.x et swagger 5.x.

il pourrait être plus efficace de ne pas rechercher le type enum deux fois dans certains cas.

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

pour utiliser le filtre ajouter c.DocumentFilter<SwaggerAddEnumDescriptions>();à la configuration swagger dans Startup.cs.

Matyas
la source
0

SOLUTION ASP NET

Dans mes documents API, un enum était toujours affiché comme int malgré le marquage de la propriété StringEnumConverter. Nous ne pouvions pas nous permettre d'utiliser le paramètre global pour toutes les énumérations mentionnées ci-dessus. L'ajout de cette ligne dans SwaggerConfig a résolu le problème:

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

Il y avait un certain nombre de lacunes que j'ai trouvées dans les autres réponses pour ce que nous recherchions, alors j'ai pensé que je fournirais ma propre opinion à ce sujet. Nous utilisons ASP.NET Core 3.1 avec System.Text.Json, mais notre approche fonctionne quel que soit le sérialiseur JSON utilisé.

Notre objectif était d'accepter les valeurs de chaîne d'énumération à casse inférieure dans l'API ASP.NET Core et de documenter la même chose dans Swagger. Nous utilisons actuellement [DataContract]et[EnumMember] , donc l'approche consiste à prendre la valeur inférieure de la casse de chameau de la propriété de valeur EnumMember et à l'utiliser dans tous les domaines.

Notre exemple d'énumération:

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

Nous utiliserons les valeurs EnumMember dans Swashbuckle en utilisant un ISchemaFilter comme dans l'exemple suivant:

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

Nous utilisons un package NuGet tiers (GitHub repo pour faire en sorte que ce système de nommage est également utilisé dans ASP.NET de base). Configurez-le dans Startup.cs dans ConfigureServices avec:

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

Enfin, nous devons enregistrer notre ISchemaFilter dans Swashbuckle, alors ajoutez également ce qui suit dans ConfigureServices ():

services.AddSwaggerGen(c => {
  c.SchemaFilter<DescribeEnumMemberValues>();
});
Xaniff
la source
GetMembers()serait préférable de GetMembers(BindingFlags.Static | BindingFlags.Public)se limiter aux seules propriétés d'énumération déclarées telles que "Blue". J'ai également adapté le cas "else" pour renvoyer le Member.Name s'il n'y a pas d' [EnumMember]attribut.
user2864740
0

Cela n'est pas possible avec OpenAPI standard. Les énumérations sont décrites uniquement avec leurs valeurs de chaîne.

Heureusement, vous pouvez le faire avec certaines extensions non standard qui sont utilisées par votre générateur client.

NSwag prend en charge x-enumNames

AutoRest prend en charge x-ms-enum.

Prise en charge d'Openapi-Generator x-enum-varnames

D'autres générateurs peuvent prendre en charge l'une de ces extensions ou posséder la leur.

Pour générer x-enumNamespour NSwag, créez le filtre de schéma suivant:

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

Et enregistrez-le comme:

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