.NET 9 的 Microsoft.AspNetCore.OpenApi:內建 API 文件生成與多樣化 UI 整合實戰

ASP.NET Core 9,微軟終於把 OpenAPI 文件生成納入框架內建功能,推出了 Microsoft.AspNetCore.OpenApi。這代表我們不再需要額外的第三方套件,就能直接在專案中生成 OpenAPI 文件,並且支援最新的 OpenAPI 3.1 與 JSON Schema draft 2020-12。

畫一張 Microsoft.AspNetCore.OpenApi 整合以下的圖:Swagger UI、Scalar UI、Redoc、RapiDoc、Elements,風格為技術架構圖,呈現各 UI 如何掛載在 Microsoft.AspNetCore.OpenApi 上

在過去的 ASP.NET Core 專案中,微軟預設使用 Swashbuckle.AspNetCore 來建立 OpenAPI 文件。它確實好用,但也帶來一些隱憂:

  • 額外的套件依賴
  • 在 Native AOT 下的相容性問題
  • JSON Schema 版本落後

本文將分兩個部分來說明:

  1. 如何使用 Microsoft.AspNetCore.OpenApi
  2. 如何整合多種第三方 UI 工具,讓 API 文件不只是 JSON,而是能夠被開發者與使用者友善地瀏覽。

開發環境

  • Windows 11 Pro
  • ASP.NET Core 9
  • Microsoft.AspNetCore.OpenApi 9.0.8
  • Rider 2025.2

 

Microsoft.AspNetCore.OpenApi 使用方式

套件安裝

建立一個新的 Web API 專案,並安裝套件:

dotnet new webapi -n Lab.AspNetCoreOpenApi
cd Lab.AspNetCoreOpenApi
dotnet add package Microsoft.AspNetCore.OpenApi

 

基本程式碼

在 Program.cs 中加入以下設定:

var builder = WebApplication.CreateBuilder(args);

// 註冊 OpenAPI 服務
builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    // 對應 /openapi/v1.json
    app.MapOpenApi();
}

app.MapGet("/weatherforecast", () =>
{
    var summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    return Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast(
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        )
    ).ToArray();
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

執行後,訪問 http://localhost:5036/openapi/v1.json 就能看到完整的 OpenAPI 文件。

優勢

  1. 零依賴:不需要 Swashbuckle 或 NSwag
  2. 效能更佳:與 ASP.NET Core 深度整合
  3. 支援最新標準:OpenAPI 3.1、JSON Schema 2020-12
  4. 原生 AOT 相容

整合第三方 UI 的使用方式

雖然 Microsoft.AspNetCore.OpenApi 幫我們解決了文件生成,但它並沒有內建 UI。
這時候,我們就需要自己整合 UI 工具。以下示範如何一次整合 6 種不同的 UI。

建立擴展方法

新增 Extensions/OpenApiUiExtensions.cs,集中管理 UI:

public static class OpenApiUiExtensions
{
    public static IEndpointConventionBuilder MapSwaggerUI(this IEndpointRouteBuilder endpoints) =>
        endpoints.MapGet("/swagger", () =>
            Results.Content(GenerateSwaggerHtml(), "text/html; charset=utf-8"));

    public static IEndpointConventionBuilder MapScalarUi(this IEndpointRouteBuilder endpoints) =>
        endpoints.MapGet("/scalar/{documentName}", (string documentName) =>
            Results.Content(GenerateScalarHtml(documentName), "text/html; charset=utf-8"));

    public static IEndpointConventionBuilder MapRedocUi(this IEndpointRouteBuilder endpoints) =>
        endpoints.MapGet("/redoc/{documentName}", (string documentName) =>
            Results.Content(GenerateRedocHtml(documentName), "text/html; charset=utf-8"));

    public static IEndpointConventionBuilder MapRapiDocUi(this IEndpointRouteBuilder endpoints) =>
        endpoints.MapGet("/rapidoc/{documentName}", (string documentName) =>
            Results.Content(GenerateRapiDocHtml(documentName), "text/html; charset=utf-8"));

    public static IEndpointConventionBuilder MapElementsUi(this IEndpointRouteBuilder endpoints) =>
        endpoints.MapGet("/elements/{documentName}", (string documentName) =>
            Results.Content(GenerateElementsHtml(documentName), "text/html; charset=utf-8"));

    public static IEndpointConventionBuilder MapOpenApiExplorerUi(this IEndpointRouteBuilder endpoints) =>
        endpoints.MapGet("/explorer/{documentName}", (string documentName) =>
            Results.Content(GenerateExplorerHtml(documentName), "text/html; charset=utf-8"));
}

 

實作的方式很簡單,就是載入 https://unpkg.com 公用 CDN所提供的 css/js 或使用官網提供的 css/js

private static string GenerateScalarHtml(string documentName)
{
    return $$"""
    <!doctype html>
    <html lang="zh-TW">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>API Documentation - Scalar UI</title>
    </head>
    <body>
        <script
            id="api-reference"
            data-url="/openapi/{{documentName}}.json"
            data-configuration='{"theme":"purple"}'
        ></script>
        <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
    </body>
    </html>
    """;
}

 

更多的內容到這裡看

sample.dotblog/WebAPI/Lab.AspNetCoreOpenApi/Lab.AspNetCoreOpenApi/Extensions/OpenApiUiExtensions.cs at master · yaochangyu/sample.dotblog

 

參考

aspnetcore/src/OpenApi/sample/EndpointRouteBuilderExtensions.cs at main · dotnet/aspnetcore

 

Program.cs 整合

using Lab.AspNetCoreOpenApi.Extensions;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();

    app.MapSwaggerUI();         
    app.MapScalarUi();          
    app.MapRedocUi();           
    app.MapRapiDocUi();         
    app.MapElementsUi();        
    app.MapOpenApiExplorerUi(); 

    // 導覽首頁
    app.MapApiDocsNavigator();  // 根路徑
}

app.Run();

 

UI 工具

工具路由特色適用場景
Swagger UI/swagger/v1業界標準,互動式測試日常開發
Scalar UI/scalar/v1現代化設計,紫色主題展示專案
Redoc/redoc/v1文件導向,優美排版文件閱讀
RapiDoc/rapidoc/v1Web Component,深色主題輕量整合
Elements/elements/v1Stoplight 生態系,企業級企業應用

 

根路徑

 

Swagger UI

 

Scalar

 

Redoc

 

Elements

 

RapiDoc

 

OpenApi 文檔


心得

這次實作下來,我有幾個感想:

  1. 微軟終於補上了缺口,內建 OpenAPI 文件,讓 ASP.NET Core 更完整。
  2. UI 工具百花齊放,不同團隊可以依需求選擇合適的 UI。

範例位置

sample.dotblog/WebAPI/Lab.AspNetCoreOpenApi at master · yaochangyu/sample.dotblog

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo