Swashbuckle 中枚举成员的描述





5.00/5 (7投票s)
如何在 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,我们会找到它的描述
但现在,我们所有的枚举都用数字表示
我更喜欢为枚举提供 string
值。它们至少对用户来说是有意义的,不像这些数字。
为此,我们需要对 Swashbuckle 配置进行一些更改。我安装了另一个 NuGet 包 Swashbuckle.AspNetCore.Newtonsoft
。以下是我的更改。我更改了
services.AddControllers();
to
services.AddControllers().AddNewtonsoftJson(o =>
{
o.SerializerSettings.Converters.Add(new StringEnumConverter
{
CamelCaseText = true
});
});
现在我们的枚举表示为 string
但即使是现在,我也看到了一个缺点。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
描述如下
枚举参数的描述
它看起来更好。但还不够好。我们的控制器有一个将 enum
作为参数的方法
public Task<Result> ExecuteOperation(int id, OperationType type)
让我们看看 Swagger UI 如何显示这个
正如你所看到的,这里没有 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 中参数描述现在的样子
结论
本文中提供的代码更像是一个草图,而不是最终版本。但我希望它能有所帮助,并允许你将 enum
成员的描述添加到你的 Swagger UI 中。谢谢!
你可以在我的博客上阅读更多我的文章。
附注:你可以在 GitHub 上找到项目的完整代码。