65.9K
CodeProject 正在变化。 阅读更多。
Home

Swashbuckle 中枚举成员的描述

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2021 年 4 月 16 日

CPOL

4分钟阅读

viewsIcon

48055

如何在 Swagger UI 中显示枚举成员的 XML 注释

Swagger 是一个很棒的东西!它允许我们轻松查看服务的 API,以不同的语言为其生成客户端,甚至通过 UI 使用该服务。在 ASP.NET Core 中,我们有 NuGet 包 Swashbuckle.AspNetCore 来支持 Swagger。

但对于这种实现,我有一点不喜欢。Swashbuckle 可以根据 .NET 代码中的 XML 注释显示方法、参数和类的描述。但它没有显示 enum 成员的描述。

让我向你展示我的意思。

服务创建

我创建了一个简单的 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());
    }
}

这个控制器大量使用了 enum。它将它们用作参数类型、方法结果以及更复杂对象的一部分

/// <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
}

我安装了 Swashbuckle.AspNetCore NuGet 包以支持 Swagger。现在我必须配置它。这可以在 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();

        ...
    }
}

现在我们可以启动我们的服务了。在地址 https://:5000/swagger/index.html,我们会找到它的描述

Swagger UI for the service

但现在,我们所有的枚举都用数字表示

Representation of enumerations by numbers

我更喜欢为枚举提供 string 值。它们至少对用户来说是有意义的,不像这些数字。

为此,我们需要对 Swashbuckle 配置进行一些更改。我安装了另一个 NuGet 包 Swashbuckle.AspNetCore.Newtonsoft。以下是我的更改。我更改了

services.AddControllers();

to

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

现在我们的枚举表示为 string

Representation of enumerations by strings

但即使是现在,我也看到了一个缺点。Swagger UI 没有向我显示分配给枚举成员的 XML 注释。

枚举类型的描述

让我们看看我们如何获取它们。我在互联网上搜索了一下,但几乎一无所获。虽然有一个非常有趣的 代码片段。不幸的是,它与旧版本的 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);
});

这可以使用 Swagger 的配置部分中的 SchemaFilter 方法来完成。我将 XML 注释文件的路径传递给此方法。该值将传递给 EnumTypesSchemaFilter 类的构造函数。

现在 Swagger UI 显示 enum 描述如下

XML comments for enumeration members

枚举参数的描述

它看起来更好。但还不够好。我们的控制器有一个将 enum 作为参数的方法

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

让我们看看 Swagger UI 如何显示这个

Parameter description

正如你所看到的,这里没有 enum 成员的描述。原因是我们在查看参数的描述,而不是参数类型的描述。所以这是参数的 XML 注释,而不是参数类型的 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 方法中,我们遍历所有控制器的所有方法的所有参数。不幸的是,在这个接口中,我们无法访问参数类型,只能访问该类型的模式(至少我是这么认为的)。这就是为什么我只从包含参数类型描述的 string 中剪切 enum 成员的描述。

我们的类必须以相同的方式使用 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 中参数描述现在的样子

Parameter description with variants

结论

本文中提供的代码更像是一个草图,而不是最终版本。但我希望它能有所帮助,并允许你将 enum 成员的描述添加到你的 Swagger UI 中。谢谢!

你可以在我的博客上阅读更多我的文章。

附注:你可以在 GitHub 上找到项目的完整代码。

© . All rights reserved.