Skip to content

ASP.NET Core Web API 入門心得 - 改善 Enum 註解

TLDR

  • Swashbuckle.AspNetCore 預設無法顯示 Enum 成員的 XML 註解,導致 API 文件可讀性不佳。
  • 透過實作 ISchemaFilter 介面,可以解析專案產出的 XML 文件,並將 Enum 成員的註解注入至 OpenAPI Schema 的 Description 欄位中。
  • 建議將 JsonStringEnumConverter 設定於 Program.cs 的全域設定中,而非個別 DTO 屬性,以維持 API 輸入與輸出的統一性。
  • 實作 EnumSchemaFilter 時,需注意檢查 schema.Description 是否已存在內容,避免重複加入描述。

問題情境:Swagger 無法顯示 Enum 成員註解

在 ASP.NET Core Web API 專案中,即使在 Enum 定義與 DTO 屬性上撰寫了完整的 XML 註解,Swagger UI 預設仍無法將這些註解呈現出來。這會造成開發者在使用 API 時,難以理解 Enum 參數的具體含意與對應數值。

什麼情況下會遇到這個問題:當專案使用 Swashbuckle.AspNetCore 作為 OpenAPI 文件產生工具,且 API 介面包含 enum 型別參數時。

enum property missing comments


解決方案:實作 ISchemaFilter 注入 XML 註解

為了讓 Swagger 能正確顯示 Enum 的詳細說明,可以透過實作 ISchemaFilter 來攔截 Schema 產生過程,並讀取編譯產生的 XML 文件進行內容注入。

實作 EnumSchemaFilter

此 Filter 會檢查目標型別是否為 Enum,並從 XML 文件中提取對應的 <summary> 內容,最後將其格式化後寫入 schema.Description

csharp
public class EnumSchemaFilter : ISchemaFilter {
    private readonly XDocument xmlComments;

    public EnumSchemaFilter(XDocument xmlComments) {
        this.xmlComments = xmlComments;
    }

    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));

            sb.AppendLine($"<li><b>{enumValue}[{enumMemberName}]</b>: {enumMemberDescription}</li>");
        }

        sb.AppendLine("</ul>");

        schema.Description = sb.ToString();
    }
}

註冊至 Swagger 設定

Program.cs 中,將上述 Filter 加入 AddSwaggerGen 的設定流程:

csharp
builder.Services.AddSwaggerGen(options => {
    foreach (string xmlFile in Directory.GetFiles(AppContext.BaseDirectory, "*.xml")) {
        XDocument xmlDoc = XDocument.Load(xmlFile);
        options.IncludeXmlComments(() => new XPathDocument(xmlDoc.CreateReader()), true);
        options.SchemaFilter<EnumSchemaFilter>(xmlDoc);
    }
});

小結論:透過自定義 ISchemaFilter,可以有效解決 Swagger 無法讀取 Enum 註解的問題,並提供開發者更友善的 API 文件參考。

swagger enum comments success


關於 JsonStringEnumConverter 的使用建議

什麼情況下會遇到這個問題:當開發者希望 API 傳輸時使用 Enum 的字串名稱而非數值,但又不希望破壞 Swagger 的顯示格式。

若直接在 DTO 屬性上使用 [JsonConverter(typeof(JsonStringEnumConverter))],會導致 Swagger 文件顯示混亂。建議改為在 Program.cs 中進行全域設定:

csharp
builder.Services.AddControllers()
    .AddJsonOptions(options => {
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
    });

小結論:將轉換器設定於全域,能確保 API 行為的一致性,並避免在每個 DTO 屬性重複宣告,同時配合上述的 EnumSchemaFilter,即可在 Swagger 中同時呈現 Enum 的數值與名稱對應關係。


異動歷程

    • 初版文件建立。
    • 修正 EnumSchemaFilter 會重複加入 enum 專案描述的錯誤。