Dokumentacja interfejsu API sieci Web Swagger UI Prezentować wyliczenia jako ciągi?


107

Czy istnieje sposób na wyświetlenie wszystkich wyliczeń jako wartości ciągu w swagger zamiast ich wartości int?

Chcę mieć możliwość przesyłania działań POST i umieszczania wyliczeń zgodnie z ich wartością ciągu bez konieczności przeglądania wyliczenia za każdym razem.

Próbowałem, DescribeAllEnumsAsStringsale serwer otrzymuje ciągi znaków zamiast wartości wyliczenia, czego nie szukamy.

Czy ktoś to rozwiązał?

Edytować:

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
Czy chcesz, aby schemat opisywał wartość jako ciąg znaków, a następnie wysyłał liczbę całkowitą na serwer? JSON.net będzie obsługiwał obie wartości dobrze, więc czy wersja zawierająca tylko liczby całkowite jest ostatecznym wymaganiem? Nie sądzę, że Swagger obsługuje typ wyliczenia z ciągiem i wartością całkowitą.
Hux,

1
Twoje oczekiwane zachowanie jest niejasne, czy możesz lepiej wyjaśnić, co chcesz, aby interfejs Swagger wyświetlał i co chcesz POST / PUT do swojego internetowego interfejsu API z przykładami?
Federico Dipuma

Co więcej, jeśli mam metody GET, które biorą enum w url, chcę schemat aby go opisać jako ciągi w rozwijanej listy sugerowanych wartości w dół

Dlaczego weryfikacja liczb całkowitych kończy się niepowodzeniem? Typ powinien być wyliczeniem w modelu, a program formatujący multimedia json poprawnie obsłuży ciąg lub int. Jeśli zaktualizujesz pytanie za pomocą przykładu, pomogłoby nam to zrozumieć, dlaczego walidacja kończy się niepowodzeniem.
Hux

4
Jeśli jest to wyliczenie flag, musi być liczbowe, chyba że masz zdefiniowane wartości wyliczenia dla każdej możliwej kombinacji flag. To szaleństwo, że swagger nie wyświetla ZARÓWNO nazwy i wartości dla każdego wyliczenia, a zamiast tego wyświetla samą liczbę (bezużyteczne) lub same nazwy (znowu bezużyteczne dla flag, które muszą być określone jako liczby).
Triynko

Odpowiedzi:


188

Z dokumentów :

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

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

Ponadto, jeśli chcesz, aby to zachowanie dotyczyło tylko określonego typu i właściwości, użyj StringEnumConverter:

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

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

5
to nie działa dla mnie. [EnumDataType (typeof (Priority))] [JsonConverter (typeof (StringEnumConverter))]
Lineker

@NH. tak, użyłem newtonsoft.json
Lineker

@Lineker, opublikuj swój błąd jako nowe pytanie, postępując zgodnie z tym przewodnikiem: stackoverflow.com/help/mcve
NH.

Dzięki! Myślę, że mogę po prostu zostawić również twój komentarz w źródle #thiswilldothetrick
Simon_Weaver

5
DescribeAllEnumsAsStringspracował dla właściwości obiektów, a nawet parametrów zapytań dotyczących akcji kontrolera. Jednak używanie EnumDataTypeAttributei JsonConverter(typeof(StringEnumConverter))nie działa dla mnie.
bugged87

93

Dla ASP.NET Core 3 z biblioteką Microsoft JSON (System.Text.Json)

W Startup.cs / ConfigureServices ():

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

W przypadku ASP.NET Core 3 z biblioteką Json.NET (Newtonsoft.Json)

Zainstaluj Swashbuckle.AspNetCore.Newtonsoftpakiet.

W Startup.cs / ConfigureServices ():

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

W przypadku ASP.NET Core 2

W Startup.cs / ConfigureServices ():

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

Pre-ASP.NET Core

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

4
Problem z używaniem options.SerializerSettings.Converters.Add (new StringEnumConverter ())) polega na tym, że zmieniasz json dla wszystkich metod, a nie tylko dla Sawshbuckle.
Guillaume

Czy ktoś ma rozwiązanie dla Azure Functions v2 i / lub v3?
Dan Friedman

@DanFriedman Biorąc pod uwagę, że Swashbuckle w ogóle nie działa z Azure Functions, nie masz szczęścia.
Ian Kemp

@IanKemp Pakiet jest obsługiwany przez osoby trzecie, AzureExtensions.Swashbuckleale podobnie jak @DanFriedman nie mogę sprawić, aby enum-to-string działało zgodnie z oczekiwaniami
wolfyuk

40

Myślę więc, że mam podobny problem. Szukam swaggera do generowania wyliczeń wraz z mapowaniem ciągów int ->. Interfejs API musi akceptować int. Swagger-ui ma mniejsze znaczenie, to, czego naprawdę chcę, to generowanie kodu z "prawdziwym" wyliczeniem po drugiej stronie (aplikacje na Androida używające w tym przypadku retrofitu).

Z moich badań wynika, że ​​ostatecznie wydaje się, że jest to ograniczenie specyfikacji OpenAPI, której używa Swagger. Nie można określić nazw ani numerów wyliczeń.

Najlepszym problemem, jaki znalazłem, jest https://github.com/OAI/OpenAPI-Specification/issues/681, który wygląda na „może wkrótce”, ale wtedy Swagger musiałby zostać zaktualizowany, aw moim przypadku Swashbuckle jako dobrze.

Na razie moim obejściem było zaimplementowanie filtru dokumentu, który wyszukuje wyliczenia i wypełnia odpowiedni opis zawartością wyliczenia.

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

}

Skutkuje to czymś podobnym do następującego w twoim swagger-ui, więc przynajmniej możesz „zobaczyć, co robisz”: wprowadź opis obrazu tutaj


1
+1 Chciałem dodać opisy do wyliczeń (po prostu „opisać wyliczenie”), nigdy o tym nie myślałem. Mam już różne filtry na miejscu, ale szukałem czegoś bardziej „organicznego”, ale nie ma wsparcia. No to filtruje całą drogę :)
NSGaga-głównie-nieaktywny

Dzięki! Użyłem tego w moim projekcie, ale zmodyfikowałem go do pracy z .NET Core. Jako odpowiedź dodałem swoją implementację.
Gabriel Luci

27

ASP.NET Core 3.1

Aby wygenerować wyliczenia jako ciągi przy użyciu Newtonsoft JSON, należy jawnie dodać obsługę Newtonsoft, dodając AddSwaggerGenNewtonsoftSupport()następujące elementy:

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


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

Funkcja ta jest dostępna za pośrednictwem nowego pakietu Swashbuckle.AspNetCore.Newtonsoft. Wygląda na to, że wszystko inne działa dobrze bez tego pakietu, z wyjątkiem obsługi konwertera wyliczeń.


1
Skonfigurowanie tej konwencji na całym świecie jest pomocne, ale jeśli chcesz zastosować ją tylko do niektórych typów wyliczeń, musisz uważnie przeczytać ten problem . TL; DR: Nie jest możliwe zastosowanie nowej StringEnumConverter () tylko do właściwości, ale można ją zastosować do całego typu wyliczenia.
A. Tretiakov

1
Przypuszczam, że jeśli mówimy o pułapkach, nie można również użyć całkowicie niestandardowego konwertera. Swagger nie uruchamia wartości wyliczenia za pośrednictwem konwertera niestandardowego; po prostu rozpoznaje StringEnumConverterjako przypadek specjalny.
Roman Starkov

22

Chciałem użyć odpowiedzi rory_za w aplikacji .NET Core, ale musiałem ją nieco zmodyfikować, aby działała. Oto implementacja, którą wymyśliłem dla .NET Core.

Zmieniłem go również, aby nie zakładał, że typ bazowy to int, i używam nowych linii między wartościami, aby ułatwić czytanie.

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

Następnie dodaj to do swojej ConfigureServicesmetody w Startup.cs:

c.DocumentFilter<EnumDocumentFilter>();

Czy można usunąć Enum: Array [6], który pojawia się poniżej?
Softlion,

4
Świetne rozwiązanie, ale rozszerzenia DescribeEnumParametersw moim projekcie były puste. Musiałem rzucić paramdo NonBodyParameteri sprawdzić tam enum:if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true) { param.Description += DescribeEnum(nbParam.Enum); }
Rabban

W moim projekcie Extensions też jest pusty, użyłem rozwiązania @Rabban.
Carlos Beppler

1
@Rabban Zaktualizowałem mój kod, aby to uwzględnić. Czy możesz po prostu sprawdzić, czy umieściłem go we właściwym miejscu? Nie miałem tego problemu. Może nowsza wersja coś zmieniła.
Gabriel Luci

@GabrielLuci Potwierdzone i zatwierdzone;)
Rabban

12

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

Wygląda jednak na to, że Swashbuckle w wersji 5.0.0-rc4 nie jest na to gotowy. Musimy więc użyć opcji (przestarzałej) w pliku konfiguracyjnym Swashbuckle, dopóki nie będzie obsługiwać i odzwierciedlać ją jak bibliotekę Newtonsoft.

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

Różnica między tą odpowiedzią a innymi odpowiedziami polega na używaniu tylko biblioteki Microsoft JSON zamiast Newtonsoft.


Hej @Bashir, czy istnieje problem ze swachbuckle, aby śledzić brak tego wsparcia?
Bernard Vander Beken

Cześć @ bernard-vander-beken, nie zgłosiłem tego, ale zakładam, że tak. Dobrze, jeśli uda nam się go znaleźć i dodać do tego postu w celu późniejszych aktualizacji.
Bashir Momen


10

NET CORE 3.1 i SWAGGER 5

jeśli potrzebujesz prostego rozwiązania do selektywnego tworzenia wyliczeń przekazywanych jako ciągi:

using System.Text.Json.Serialization;


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

Uwaga, używamy System.Text.Json.Serializationprzestrzeni nazw, a nie Newtonsoft.Json!


Ten działa, pokazując prawidłowe wartości, a także działa podczas konwersji wartości z powrotem na wyliczenie. Pamiętaj, że musisz dodać pakiet NuGet System.Text.Json.
MovGP0

Właśnie tego szukałem! Ponieważ muszę użyć ciągu tylko dla pojedynczego wyliczenia i DescribeAllEnumsAsStringsprzekonwertuje wszystkie wyliczenia na ciąg.
Nilay

9

jeśli ktoś jest zainteresowany, zmodyfikowałem kod do pracy

.NET CORE 3 i 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
Działa to tylko wtedy, gdy typ parametru jest dokładnie wyliczony ... wyliczenie nie dopuszczające wartości null, zbiór wyliczeń itp. Sprawdź moją odpowiedź w tych przypadkach.
Matyas

4

Właśnie to zrobiłem i działa dobrze!

Startup.cs

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

Model.cs

public enum ColumnType {
  DATE = 0
}

swagger.json

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

Mam nadzieję, że pomogło ci to, jak mi pomogło!


2
DescribeAllEnumsAsStringsjest przestarzały
Node.JS

4

w .net core 3.1 i 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++;
                }
            }
        }
    }

}

oraz w Startup.cs:

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

Wynik


4
Wadą tego jest to, że podczas wykonywania żądania, zamiast przekazywania tylko reprezentacji int (na przykład 2) wartości wyliczenia, API otrzyma pełny opis jako wartość (jak LogicError = 3), co zakończy się niepowodzeniem jako złe żądanie, ponieważ nie jest to poprawna wartość wyliczenia.
Matyas

3

Mój wariant dla wyliczenia żądeł z wartościami:

wprowadź opis obrazu tutaj

Skonfiguruj usługi:

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

Filtr:

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

napisz kod wewnątrz Startup.cs

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

2
Ta opcja jest przestarzała w Swashbuckle. Zaleca się użycie opcji ASP.NET Core, a następnie Swashbuckle może to odzwierciedlić.
Bashir Momen

2

Znalazłem fajne obejście tutaj:

@PauloVetor - rozwiązałem to za pomocą ShemaFilter w ten sposób:

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

A w Startup.cs:

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

Powinieneś także upewnić się, że zaktualizujesz model.Formatdo "string"tak, jak zwykle "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
Używa Newtonsoft zamiast nowej serializacji asp.net core JSON.
Bashir Momen

1

Zmodyfikowałem odpowiedź Hosam Rehani, aby działała z wyliczeniami zerowalnymi, a także z kolekcją wyliczeń. Poprzednia odpowiedź działa również tylko wtedy, gdy właściwość jest nazwana dokładnie tak, jak jej typ. Wszystkie te problemy są opisane w poniższym kodzie.

Działa z .net core 3.x i swagger 5.x.

w niektórych przypadkach może być bardziej wydajne, jeśli nie wyszukuje się typu wyliczenia dwukrotnie.

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

aby użyć filtra dodaj c.DocumentFilter<SwaggerAddEnumDescriptions>();do konfiguracji swagger w Startup.cs.


0

ROZWIĄZANIE ASP NET

W moich dokumentach API jedno wyliczenie było nadal wyświetlane jako int, mimo że właściwość została oznaczona StringEnumConverter. Nie mogliśmy sobie pozwolić na używanie ustawienia globalnego dla wszystkich wymienionych powyżej wyliczeń. Dodanie tej linii w SwaggerConfig rozwiązało problem:

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

0

Było wiele niedociągnięć, które znalazłem w innych odpowiedziach na to, czego szukaliśmy, więc pomyślałem, że przedstawię własne podejście. Używamy ASP.NET Core 3.1 z System.Text.Json, ale nasze podejście działa niezależnie od używanego serializatora JSON.

Naszym celem było zaakceptowanie wartości ciągów wyliczeniowych z małymi wielbłądami w obu ASP.NET Core API, a także udokumentowanie tego samego w Swagger. Obecnie korzystamy z [DataContract]i [EnumMember], więc podejście polega na przejęciu mniejszej wartości wielkości wielbłąda z właściwości wartości EnumMember i zastosowaniu jej we wszystkich kategoriach.

Nasze przykładowe wyliczenie:

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

Użyjemy wartości EnumMember w Swashbuckle przy użyciu ISchemaFilter, jak w poniższym przykładzie:

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

Używamy pakietu NuGet innej firmy ( repozytorium GitHub ), aby upewnić się, że ten schemat nazewnictwa jest również używany w ASP.NET Core. Skonfiguruj go w Startup.cs w ConfigureServices za pomocą:

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

Na koniec musimy zarejestrować nasz ISchemaFilter w Swashbuckle, więc dodaj również następujące elementy w ConfigureServices ():

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

GetMembers()byłoby lepiej GetMembers(BindingFlags.Static | BindingFlags.Public)ograniczyć się tylko do faktycznie zadeklarowanych właściwości wyliczenia, takich jak „Niebieski”. Dostosowałem również przypadek „else”, aby zwracał element Member.Name, jeśli nie ma [EnumMember]atrybutu.
user2864740

0

Nie jest to możliwe w przypadku standardowego OpenAPI. Wyliczenia są opisywane tylko za pomocą wartości łańcuchowych.

Na szczęście możesz to zrobić za pomocą niestandardowych rozszerzeń, które są używane przez generator klientów.

NSwag obsługuje x-enumNames

Obsługuje AutoRest x-ms-enum.

Obsługuje Openapi-generator x-enum-varnames

Inne generatory mogą obsługiwać jedno z tych rozszerzeń lub mieć własne.

Aby wygenerować x-enumNamesdla NSwag, utwórz następujący filtr schematu:

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

I zarejestruj to jako:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.