Как получить документацию перечисления в Swagger

Я работаю с документацией по API Swagger и хочу, чтобы мое описание перечислений было в документации. Я использую собственный фильтр для извлечения информации из классов и перечислений с помощью аннотаций. например.:

    using System.ComponentModel;
    ...
    [DisplayName("Upload class")]            // Works fine
    public class Upload
    {
        [DisplayName("Primary Key")]          // Works fine
        public int UploadMetaId { get; set; }
        [Display(Name = "document name")]     // Works fine too
        public string Title { get; set; }
    }

Но у меня проблемы с моими Enums:

[DisplayName("Document types")]    // Illegal, gives error CS0592
// or
[Display(Name = "Document types")]  // Illegal too, gives also CS0592
public enum UploadType
{
    [Display(Name = "Årsopgørelse")] // Works fine
    PartnerAarsopgoerelse = 1,
    [Display(Name = "Ægtefælles årsopgørelse")]
    AegtefaelleAarsopgoerelse = 2
}

Ошибка CS0592 говорит о том, что она недействительна для этого типа объявления.

Итак, что я могу использовать вместо этого?

ОБНОВЛЯТЬ

Я использую следующие пакеты NuGet:

  • Microsoft.OpenApi (1.6.15)
  • SwashBucle.AspNetCore(6.6.2)

Перечисления в C# не являются классами, они, по сути, помечены целыми числами.

Panagiotis Kanavos 09.07.2024 10:37

Ответ может зависеть от пакета, генерирующего описание Open API. Swashbuckle, NSwag и т. д.

Ralf 09.07.2024 10:45
Это может помочь.
Ralf 09.07.2024 11:25
DisplayAttribute и DisplayNameAttribute не работают с типами перечислений, им не хватает AttributeTargets.Enum в определении AttributeUsage. Learn.microsoft.com/en-us/dotnet/api/… Learn.microsoft.com/en-us/dotnet/api/…
lidqy 09.07.2024 15:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
95
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Благодаря @ralf и статье в его комментарии я создал решение с помощью собственного IDocumentFilter.

Я надеюсь, что смогу спасти кого-то от большого количества поисков, используя приведенный ниже код.

Я программирую.cs. Я беру XML-документацию из проекта и передаю соответствующую «сводку» в IDocumentFilter:

 private static void ConfigureServices(IServiceCollection services)
 {
     ...
     services.AddSwaggerGen(c =>
     {
         ...
         Dictionary<string, string> Dict = new Dictionary<string, string>();
         var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
         foreach (var filePath in System.IO.Directory.GetFiles(dir, "*.xml"))
         {
             c.IncludeXmlComments(filePath);
             GetXMLDoc(filePath, Dict);          
         }
         c.DocumentFilter<SwaggerEnumDocumentFilter>(Dict);               
     });
 }

private static void GetXMLDoc(string path, Dictionary<string,string> Dict)
{
    var _xmlComments = new XmlDocument();
    var reader = XmlReader.Create(path);
    _xmlComments.Load(reader);
    var xpath = $"//member[@name[starts-with(.,'F')] or @name[starts-with(.,'T')]]";
    var nodes = _xmlComments.DocumentElement.SelectNodes(xpath);
    foreach(XmlNode node in nodes)
    {
        var attribute = node.Attributes["name"];
        if (attribute != null)
        {
            string summary = node.FirstChild.InnerText.Trim(' ','\n');
            Dict.Add(attribute.Value, summary);
        }
    }
}

В фильтре подбираю XML-документацию из переданного словаря:

public class SwaggerEnumDocumentFilter : IDocumentFilter
{

    Dictionary<string, string> EnumDescription = new Dictionary<string, string>();

    public SwaggerEnumDocumentFilter(Dictionary<string, string> enumDescription)
    {
        EnumDescription = enumDescription;
    }

    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        // add enum descriptions Schemas section in Swagger
        foreach (var property in swaggerDoc.Components.Schemas)
        {
            var propertyEnums = property.Value.Enum;
            if (propertyEnums is { Count: > 0 })
            {
                property.Value.Description += DescribeEnum(property.Key, false);
            }
        }

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

        //add enum descriptions to input parameters
        foreach (var pathItem in swaggerDoc.Paths.Values)
        {
            foreach (var operation in pathItem.Operations)
            {
                if (operation.Value.Parameters != null)
                {
                    foreach (var param in operation.Value.Parameters)
                    {
                        if (param.Schema.Reference != null)
                        {
                            param.Description += DescribeEnum(param.Schema.Reference.Id, true);
                        }
                    }
                }
            }
        }
    }

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

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

    private string DescribeEnum(string propertyTypeName, bool inApiList)
    {
        var enumType = GetEnumTypeByName(propertyTypeName);

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

        var values = Enum.GetValues(enumType);
        if (values == null)
        {
            return null;
        }

        string result = "<ul>";

        if (inApiList)
        {
            var key = $"T:{enumType.FullName}";
            var summary = EnumDescription.ContainsKey(key) ? EnumDescription[key] : "";
            result = $"<p>{summary}</p><ul>";
        }

        foreach (var value in values)
        {
            var key = $"F:{enumType.FullName}.{value}";
            var summary = EnumDescription.ContainsKey(key) ? EnumDescription[key] : "";
            string n = EnumUtils.GetDisplayName((System.Enum)value);
            result += $"<li>{(int)value} - {n} : {summary} </li>";
        }
        result += "</ul>";
        return result;
    }

}

Другие вопросы по теме