# ASP.NET Core Web API 入門心得 - 改善 Enum 註解
[](https://hackmd.io/9ZHTEF3yT2aGxEOwhzunjA)
在建立 ASP.NET Core Web API 專案時,透過「啟用 OpenAPI 支援」的設定,可自動安裝 `Swashbuckle.AspNetCore` 這套 Swagger 套件,但目前該套件尚不支援 Enum 的註解功能,這導致使用端難以準確理解如何傳遞參數和回傳值的含意,與回傳值含意。即使在具有 Enum 的屬性增加屬性的註解,也依然沒作用。
以下列程式碼作為範例:
```csharp
[ApiController]
[Route("[controller]")]
public class TestEnumController : ControllerBase {
[HttpPost]
public Output Post([FromBody] Input input) {
return new Output {
Week = input.Week,
};
}
}
/// <summary>
/// The input.
/// </summary>
public class Input {
/// <summary>
/// Gets or sets the week.
/// 0: Sunday
/// 1: Monday
/// 2: Tuesday
/// 3: Wednesday
/// 4: Thursday
/// 5: Friday
/// 6: Saturday
/// </summary>
public Week Week { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name { get; set; }
}
/// <summary>
/// The Output.
/// </summary>
public class Output {
/// <summary>
/// Gets or sets the week.
/// </summary>
public Week Week { get; set; }
}
/// <summary>
/// 星期
/// </summary>
public enum Week {
/// <summary>
/// The sunday
/// </summary>
Sunday,
/// <summary>
/// The monday
/// </summary>
Monday,
/// <summary>
/// The tuesday
/// </summary>
Tuesday,
/// <summary>
/// The wednesday
/// </summary>
Wednesday,
/// <summary>
/// The thursday
/// </summary>
Thursday,
/// <summary>
/// The friday
/// </summary>
Friday,
/// <summary>
/// The saturday
/// </summary>
Saturday
}
```
可以看到 `Input` 的 `name` 屬性有註解,但 `week` 屬性卻沒有 Enum 的註解,也沒有屬性的註解。

有些人會選擇用 Enum 名稱 來取代 Enum 值,藉由使用 `JsonStringEnumConverter` 來讓 Enum 支援字串輸入與輸出,將程式碼調整如下:
```csharp
/// <summary>
/// The input.
/// </summary>
public class Input {
/// <summary>
/// Gets or sets the week.
/// 0: Sunday
/// 1: Monday
/// 2: Tuesday
/// 3: Wednesday
/// 4: Thursday
/// 5: Friday
/// 6: Saturday
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public Week Week { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name { get; set; }
}
/// <summary>
/// The Output.
/// </summary>
public class Output {
/// <summary>
/// Gets or sets the week.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public Week Week { get; set; }
}
```
這樣一來,Enum 的輸入方式從僅支援 Enum 值,擴充為同時支援 Enum 名稱,且回傳值也轉為Enum 名稱。但 Swagger 說明依然沒變。

為了改善這個問題,把 DTO 設定的 `JsonStringEnumConverter` 移除,改在使用 `Program` 設定。
```csharp
builder.Services.AddControllers()
.AddJsonOptions(options => {
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
}
);
```
這樣的調整,輸入和輸出結果與在屬性設定的結果一致,但是 Swagger 說明從 `int` 陣列,變成 `string` 陣列,如下圖所示:

雖然這樣的作法讓輸入增加彈性,變成 Enum 值和 Enum 名稱都支援,但回傳值變成 Enum 名稱有好有壞,好處是至少比數值更容易知道意思,但是字串有增加使用端打錯的可能,至少以我個人想法,更傾向用 Enum 值傳遞,因此還是要想辦法讓 Swagger 說明能顯示 Enum 註解。
後續參考此篇文章 [SwaggerUI not display enum summary description, C# .net core?](https://stackoverflow.com/questions/53282170/swaggerui-not-display-enum-summary-description-c-sharp-net-core) 進行以下調整:
```csharp
/// <summary>
/// Swagger schema filter to modify description of enum types so they
/// show the XML docs attached to each member of the enum.
/// </summary>
public class EnumSchemaFilter : ISchemaFilter {
private readonly XDocument xmlComments;
/// <summary>
/// Initialize schema filter.
/// </summary>
/// <param name="xmlComments">Document containing XML docs for enum members.</param>
public EnumSchemaFilter(XDocument xmlComments) {
this.xmlComments = xmlComments;
}
/// <summary>
/// Apply this schema filter.
/// </summary>
/// <param name="schema">Target schema object.</param>
/// <param name="context">Schema filter context.</param>
public void Apply(OpenApiSchema schema, SchemaFilterContext context) {
Type enumType = context.Type;
if (!enumType.IsEnum) {
return;
}
// 避免重複加入描述
if (schema.Description?.Contains("<p>Possible values:</p>") == true) {
return;
}
StringBuilder sb = new(schema.Description);
sb.AppendLine("<p>Possible values:</p>");
sb.AppendLine("<ul>");
foreach (string enumMemberName in Enum.GetNames(enumType)) {
string fullEnumMemberName = $"F:{enumType.FullName}.{enumMemberName}";
string enumMemberDescription = xmlComments.XPathEvaluate(
$"normalize-space(//member[@name = '{fullEnumMemberName}']/summary/text())"
) as string;
if (string.IsNullOrEmpty(enumMemberDescription)) {
continue;
}
long enumValue = Convert.ToInt64(Enum.Parse(enumType, enumMemberName));
// 實際要使用 Enum 值還是 Enum 名稱,自行評估
sb.AppendLine($"<li><b>{enumValue}[{enumMemberName}]</b>: {enumMemberDescription}</li>");
}
sb.AppendLine("</ul>");
schema.Description = sb.ToString();
}
}
// Program 增加自訂 Swagger Filter 設定
builder.Services.AddSwaggerGen(options => {
foreach (string xmlFile in Directory.GetFiles(AppContext.BaseDirectory, "*.xml")) {
// 從單純 IncludeXml,改為是讀 XML,用 Filter 修改
XDocument xmlDoc = XDocument.Load(xmlFile);
options.IncludeXmlComments(() => new XPathDocument(xmlDoc.CreateReader()), true);
options.SchemaFilter<EnumSchemaFilter>(xmlDoc);
}
});
```
顯示結果如下圖,成功顯示 Enum 的註解。

網路上還有另一篇是在 [替 swagger 加上具有可讀性的 enum 文件](https://dotblogs.azurewebsites.net/AceLee/2023/12/07/170518) `ISchemaFilter` 的 `Apply` 方法裡針對 `schema.Enum` 處理,但這會影響 Swagger 輸入的預設值。此作法結果如下:

Swagger Input 的預設值,是顯示 `schema.Enum` 裡的第一個 `OpenApiString`。

## 其他相關文章
* [ASP.NET Core Web API 入門心得](https://hackmd.io/@CloudyWing/HJ-KKurHp)
* [ASP.NET Core Web API 入門心得 - Middleware 順序](https://hackmd.io/@CloudyWing/H19VeBll0)
## 異動歷程
* 2024-04-13 新增。
* 2025-01-16 修正 EnumSchemaFilter 會重複加入 enum 項目描述的錯誤。
###### tags: `.NET` `.NET Core & .NET 5+` `Web API` `Swagger`