Русский
Русский
English
Статистика
Реклама

Перевод Описание элементов перечислений в Swashbuckle

Swagger замечательная вещь! Он позволяет легко посмотреть, каким API обладает ваш сервис, сгенерировать клиента для него на различных языках и даже попробовать поработать с сервисом через UI. В ASP.NET Core для поддержки Swagger существует пакет Swashbuckle.AspNetCore.


Но есть один недостаток, который мне не нравится. Swashbuckle способен строить описания методов, параметров и классов, основываясь на XML-комментариях в коде .NET. Но он не показывает те описания, которые применяются непосредственно к членам перечислений.


Позвольте мне показать, о чём идёт речь.


Создание сервиса


Я создал простой Web-сервис:


/// <summary>/// Contains endpoints that use different enums./// </summary>[Route("api/data")][ApiController]public class EnumsController : ControllerBase{    /// <summary>    /// Executes operation of requested type and returns result status.    /// </summary>    /// <param name="id">Operation id.</param>    /// <param name="type">Operation type.</param>    /// <returns>Result status.</returns>    [HttpGet]    public Task<Result> ExecuteOperation(int id, OperationType type)    {        return Task.FromResult(Result.Success);    }    /// <summary>    /// Changes data    /// </summary>    [HttpPost]    public Task<IActionResult> Change(DataChange change)    {        return Task.FromResult<IActionResult>(Ok());    }}

Этот контроллер использует перечисления во множестве мест: в качестве аргументов методов, в качестве результатов работы методов, в качестве типов свойств более сложных объектов:


/// <summary>/// Operation types./// </summary>public enum OperationType{    /// <summary>    /// Do operation.    /// </summary>    Do,    /// <summary>    /// Undo operation.    /// </summary>    Undo}/// <summary>/// Operation results./// </summary>public enum Result{    /// <summary>    /// Operations was completed successfully.    /// </summary>    Success,    /// <summary>    /// Operation failed.    /// </summary>    Failure}/// <summary>/// Data change information./// </summary>public class DataChange{    /// <summary>    /// Data id.    /// </summary>    public int Id { get; set; }    /// <summary>    /// Source type.    /// </summary>    public Sources Source { get; set; }    /// <summary>    /// Operation type.    /// </summary>    public OperationType Operation { get; set; }}/// <summary>/// Types of sources./// </summary>public enum Sources{    /// <summary>    /// In-memory data source.    /// </summary>    Memory,    /// <summary>    /// Database data source.    /// </summary>    Database}

Для поддержки Swagger я установил в проект NuGet-пакет Swashbuckle.AspNetCore. Теперь его нужно подключить. Это делается в Startup-файле:


public class Startup{    // This method gets called by the runtime. Use this method to add services to the container.    public void ConfigureServices(IServiceCollection services)    {        services.AddControllers();        services.AddSwaggerGen(c => {            // Set the comments path for the Swagger JSON and UI.            var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";            var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);            c.IncludeXmlComments(xmlPath);        });    }    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)    {        if (env.IsDevelopment())        {            app.UseDeveloperExceptionPage();        }        app.UseSwagger();        app.UseSwaggerUI();        app.UseRouting();        ...    }}

Теперь мы можем запустить наше приложение, и по адресу http://localhost:5000/swagger/index.html мы найдём описание нашего сервиса:


Swagger UI для приложения


Но пока наши перечисления представлены просто числами:


Представление перечислений числами


Лично мне было бы удобно, если бы перечисления представлялись строковыми значениями. Они хотя бы имеют некоторый смысл в отличии от безликих чисел.


Для этого нам нужно внести небольшие изменения в настройку нашего сервиса. Я установил NuGet-пакет Swashbuckle.AspNetCore.Newtonsoft. После этого, я чуть изменил настройки сервисов. Я заменил


services.AddControllers();

на


services.AddControllers().AddNewtonsoftJson(o =>{    o.SerializerSettings.Converters.Add(new StringEnumConverter    {        CamelCaseText = true    });});

Теперь наши перечисления представлены в виде строк:


Представление перечислений строками


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


Описание типов перечислений


Давайте посмотрим, как нам вернуть их. Большей частью поиск в интернете на эту тему ничего не дал мне. Но, в конце концов, я нашёл интересный код. К сожалению, он относится к старой версии Swashbuckle. Но он послужил мне хорошей отправной точкой.


Swashbuckle предоставляет возможность вмешиваться в процесс генерации документации. В частности, существует интерфейс ISchemaFilter, который позволяет изменять описания схем отдельных классов. Следующий код показывает, как изменить описание классов перечислений:


public class EnumTypesSchemaFilter : ISchemaFilter{    private readonly XDocument _xmlComments;    public EnumTypesSchemaFilter(string xmlPath)    {        if(File.Exists(xmlPath))        {            _xmlComments = XDocument.Load(xmlPath);        }    }    public void Apply(OpenApiSchema schema, SchemaFilterContext context)    {        if (_xmlComments == null) return;        if(schema.Enum != null && schema.Enum.Count > 0 &&            context.Type != null && context.Type.IsEnum)        {            schema.Description += "<p>Members:</p><ul>";            var fullTypeName = context.Type.FullName;            foreach (var enumMemberName in schema.Enum.OfType<OpenApiString>().Select(v => v.Value))            {                var fullEnumMemberName = $"F:{fullTypeName}.{enumMemberName}";                var enumMemberComments = _xmlComments.Descendants("member")                    .FirstOrDefault(m => m.Attribute("name").Value.Equals(fullEnumMemberName, StringComparison.OrdinalIgnoreCase));                if (enumMemberComments == null) continue;                var summary = enumMemberComments.Descendants("summary").FirstOrDefault();                if (summary == null) continue;                schema.Description += $"<li><i>{enumMemberName}</i> - {summary.Value.Trim()}</li>";            }            schema.Description += "</ul>";        }    }}

Конструктор этого класса принимает имя файла с XML-комментариями. Его содержимое для удобства работы читается в экземпляр XDocument. Затем в методе Apply мы проверяем, генерируется ли схема для перечисления или нет. Если это схема перечисления, то мы добавляем к описанию класса HTML-список, содержащий описание каждого используемого члена перечисления.


Теперь наш класс нужно подключить, чтобы Swashbuckle знал о нём:


services.AddSwaggerGen(c => {    // Set the comments path for the Swagger JSON and UI.    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);    c.IncludeXmlComments(xmlPath);    c.SchemaFilter<EnumTypesSchemaFilter>(xmlPath);});

Это делается с помощью метода SchemaFilter в настройках Swagger. Этому методу в качестве параметра передаётся имя файла с XML-комментариями. Именно оно будет передано в конструктор нашего класса EnumTypesSchemaFilter.


Теперь в Swagger UI описания классов-перечислений выглядят так:


XML-комментарии к членам перечисления


Описание перечислений в параметрах


Это уже лучше. Но всё же не достаточно хорошо. У нас в контроллере есть метод, который принимает перечисление в качестве параметра:


public Task<Result> ExecuteOperation(int id, OperationType type)

Давайте посмотрим, как выглядит его описание в Swagger UI:


Описание параметра


Как видите, здесь не присутствует никакого описания членов перечисления. Причина в том, что здесь мы видим описание параметра, а не его типа. Т.е. вы видите XML-комментарий, соответствующий параметру метода, а не типу этого параметра.


Но и эту проблему можно решить. Для этого воспользуемся другим интерфейсом Swashbuckle IDocumentFilter. Вот его реализация:


public class EnumTypesDocumentFilter : IDocumentFilter{    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)    {        foreach (var path in swaggerDoc.Paths.Values)        {            foreach(var operation in path.Operations.Values)            {                foreach(var parameter in operation.Parameters)                {                    var schemaReferenceId = parameter.Schema.Reference?.Id;                    if (string.IsNullOrEmpty(schemaReferenceId)) continue;                    var schema = context.SchemaRepository.Schemas[schemaReferenceId];                    if (schema.Enum == null || schema.Enum.Count == 0) continue;                    parameter.Description += "<p>Variants:</p>";                    int cutStart = schema.Description.IndexOf("<ul>");                    int cutEnd = schema.Description.IndexOf("</ul>") + 5;                    parameter.Description += schema.Description                        .Substring(cutStart, cutEnd - cutStart);                }            }        }    }}

Здесь в методе Apply мы перебираем все параметры всех методов всех контроллеров. К сожалению, здесь Swashbuckle API не даёт доступа в типу параметра, а только к схеме его типа (ну или я не нашёл такой возможности). Поэтому мне пришлось вырезать описание членов перечисления из строки описания типа параметра.


Наш класс нужно зарегистрировать аналогичным образом, с помощью метода DocumentFilter:


services.AddSwaggerGen(c => {    // Set the comments path for the Swagger JSON and UI.    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);    c.IncludeXmlComments(xmlPath);    c.SchemaFilter<EnumTypesSchemaFilter>(xmlPath);    c.DocumentFilter<EnumTypesDocumentFilter>();});

Вот как выглядит теперь описание параметра в Swagger UI:


Описание параметра с доступными вариантами


Заключение


Приведённый здесь код является скорее наброском для решения проблемы, чем окончательным вариантом. Надеюсь, он будет полезен вам и позволит добавить описание членов перечисления в ваш Swagger UI. Спасибо!


P.S. Вы можете найти код проекта на GitHub.

Источник: habr.com
К списку статей
Опубликовано: 16.04.2021 14:07:36
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Net

Swagger

Swagger-ui

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru