後端 API 已經改好了,但前端卻還在用舊的呼叫方式,文件也沒有更新,這就像是兩個人在對話,一個講新語言,一個還在講舊語言,結果就會雞同鴨講。
這時候,Swagger 就像是一個貼心的小幫手 🐥。它會自動幫你把 API 說明整理好,還能變成互動式的測試工具。更重要的是,它跟 OpenAPI 規範搭配之後,文件就不再只是「寫給人看的文字」,而是一份「電腦也能讀懂的契約」。 這意味著,前後端、測試工具、甚至 SDK 產生器,都能直接根據這份文件來溝通。
🌿 套件 Swashbuckle.AspNetCore.SwaggerUi
🌿 文件 https://swagger.io/api-hub/
🌿 Swagger 與 OpenAPI 的關係
Swagger 是一個「專案 / 工具集」 最早由 Wordnik 在 2010 左右提出的 API 文件產生工具,叫做 Swagger。它的核心想法:用一份 規格文件 (JSON / YAML) 來描述 API,然後可以自動產生「文件頁面」和「測試工具」。 這份規格文件,後來被業界廣泛接受。
OpenAPI 是「規範 (specification)」 到了 2016 年,Swagger 規格捐給了 Linux Foundation,成立了 OpenAPI Initiative (OAI)。從那之後,Swagger 規格改名為 OpenAPI Specification (OAS)。 也就是說
一開始 (Swagger 1.x / 2.0),大家習慣把「規格」和「工具」都叫 Swagger。後來 (2016 年),這個規格捐給 Linux Foundation,改名叫 OpenAPI Specification (OAS),成為一個獨立的標準。 Swagger 這個名字就保留下來,專指那一組工具(Swagger UI、Swagger Editor、Swagger Codegen…)。
常見的 Swagger 工具
Swagger UI → 把 OpenAPI 文件轉成可互動的網頁
Swagger Editor → 線上編輯 OpenAPI 文件
Swagger Codegen → 依據 OpenAPI 產生客戶端 / 伺服端程式碼
Swashbuckle (ASP.NET Core 套件) → 自動把 Controller 轉成 OpenAPI
🌿 註冊 是在 ASP.NET Core 專案裡,啟用 Swagger (OpenAPI) 文件產生器的註冊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 using Microsoft.OpenApi.Models;var builder = WebApplication.CreateBuilder(args);builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1" , new OpenApiInfo { Title = "Todo API" , Version = "v1" , Description = "A simple example ASP.NET Core Web API with Swagger" }); }); var app = builder.Build();if (app.Environment.IsDevelopment()){ app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json" , "Todo API v1" ); options.RoutePrefix = string .Empty; }); } app.MapControllers(); app.Run();
AddSwaggerGen() 來自 Swashbuckle.AspNetCore 套件。它會把你的 API Controllers / Actions 掃描起來,自動產生符合 OpenAPI 規範 (Swagger) 的 API 文件。這份文件通常位於 /swagger/v1/swagger.json。
.NET 8 的新方式 在 .NET 8,微軟直接內建了 AddOpenApi() 方法,比 AddSwaggerGen() 更簡單。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var builder = WebApplication.CreateBuilder(args);builder.Services.AddOpenApi(); var app = builder.Build();if (app.Environment.IsDevelopment()){ app.MapOpenApi(); } app.MapControllers(); app.Run();
差別在於 AddSwaggerGen() 是用 Swashbuckle (第三方套件),而 AddOpenApi() → .NET 8 官方內建,預設就支援 OpenAPI,本質上都是產生 OpenAPI 文件,只是方式不同。
⚠️ 如果你同時加了 AddSwaggerGen() 和 AddOpenApi(),那麼系統就會輸出兩份 OpenAPI 文件
/swagger/v1/swagger.json
/openapi/v1.json
其實技術上是「可以並存」,但通常沒必要。
🌿 launchsettings.json 設定檔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 "profiles" : { "http" : { "commandName" : "Project" , "dotnetRunMessages" : true , "launchBrowser" : true , "launchUrl" : "swagger" , "applicationUrl" : "http://localhost:5188" , "environmentVariables" : { "ASPNETCORE_ENVIRONMENT" : "Development" } } , "https" : { "commandName" : "Project" , "dotnetRunMessages" : true , "launchBrowser" : true , "launchUrl" : "swagger" , "applicationUrl" : "https://localhost:7258;http://localhost:5188" , "environmentVariables" : { "ASPNETCORE_ENVIRONMENT" : "Development" } } , "IIS Express" : { "commandName" : "IISExpress" , "launchBrowser" : true , "launchUrl" : "swagger" , "environmentVariables" : { "ASPNETCORE_ENVIRONMENT" : "Development" } } }
🌿 為什麼需要 OpenAPI? 它是「API 的標準格式」。不管你用什麼語言(C#、Java、Python、Node.js…),只要輸出一份 OpenAPI 文件,其他工具或團隊就能直接使用,前端框架 (TypeScript, Angular, React)、測試工具 (Postman, Insomnia)、自動化 SDK 產生器 (C#, TypeScript, Java)
有了 OpenAPI,就能讓很多流程自動化
客戶端 SDK → 自動產生 C#、TypeScript、Java 的 API 呼叫程式碼
伺服端樣板 → 自動產生 Controller / Service 骨架
文件網站 → 自動產生互動式 API 文件頁面 (Swagger UI)
這樣的好處是,後端修改 API 文件自動更新、前端重新產生 SDK 自動同步
🌿 Swashbuckle 和 NSwag
🔹 Swashbuckle 來源是社群套件,ASP.NET Core 官方文件推薦使用,NuGet 套件:Swashbuckle.AspNetCore 主要用途為自動產生 OpenAPI (/swagger/v1/swagger.json)、自動掛 Swagger UI (/swagger)、支援自訂 Schema、OperationFilter、文件版本化
特色:
專注於 API → 文件(從程式碼產生文件)
ASP.NET Core 社群使用最廣,教學/範例最多
功能完整,支援多種安全性(JWT、OAuth2)
👉 最常見的選擇,如果只需要產生文件與 UI,Swashbuckle 就足夠。
自訂 Schema : 改模型描述(例:隱藏欄位、修改格式) 用途:修改 Swagger 產生的模型描述(Schema)。 情境:你有一個敏感欄位,不想出現在 API 文件裡。
1 2 3 4 5 6 public class User { public int Id { get ; set ; } public string UserName { get ; set ; } public string PasswordHash { get ; set ; } }
SchemaFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using Microsoft.OpenApi.Models;using Swashbuckle.AspNetCore.SwaggerGen;public class HidePasswordSchemaFilter : ISchemaFilter { public void Apply (OpenApiSchema schema, SchemaFilterContext context ) { if (context.Type == typeof (User)) { schema.Properties.Remove("passwordHash" ); } } } builder.Services.AddSwaggerGen(c => { c.SchemaFilter<HidePasswordSchemaFilter>(); });
OperationFilter 改 API 操作描述(例:全域加 Header、修改參數描述)
用途:修改 API 動作 (Operation) 的 Swagger 描述。 情境:幫 API 加上 全域的 Header 說明,例如「必須帶 X-Request-Id」。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class AddRequestIdHeaderFilter : IOperationFilter { public void Apply (OpenApiOperation operation, OperationFilterContext context ) { operation.Parameters.Add(new OpenApiParameter { Name = "X-Request-Id" , In = ParameterLocation.Header, Required = true , Description = "每個請求必須帶唯一 ID" , Schema = new OpenApiSchema { Type = "string" } }); } } builder.Services.AddSwaggerGen(c => { c.OperationFilter<AddRequestIdHeaderFilter>(); });
文件版本化 (API Versioning) 支援多 API 版本,Swagger UI 分組顯示 用途:讓同一個 API 系統支援多個版本(例如 v1, v2),並在 Swagger 裡分開顯示。 情境:Todo API v1 只回傳標題,Todo API v2 回傳標題 + 完成狀態。
1 2 3 4 5 6 7 8 9 10 11 12 [ApiController ] [Route("api/v{version:apiVersion}/[controller]" ) ] [ApiVersion("1.0" ) ] [ApiVersion("2.0" ) ] public class TodoController : ControllerBase { [HttpGet, MapToApiVersion("1.0" ) ] public IActionResult GetV1 () => Ok(new [] { new { Title = "Buy milk" } }); [HttpGet, MapToApiVersion("2.0" ) ] public IActionResult GetV2 () => Ok(new [] { new { Title = "Buy milk" , IsDone = false } }); }
註冊 API 版本化 + Swagger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 builder.Services.AddApiVersioning(); builder.Services.AddVersionedApiExplorer(options => { options.GroupNameFormat = "'v'VVV" ; options.SubstituteApiVersionInUrl = true ; }); builder.Services.AddSwaggerGen(options => { var provider = builder.Services.BuildServiceProvider() .GetRequiredService<IApiVersionDescriptionProvider>(); foreach (var desc in provider.ApiVersionDescriptions) { options.SwaggerDoc(desc.GroupName, new OpenApiInfo { Title = $"Todo API {desc.ApiVersion} " , Version = desc.ApiVersion.ToString() }); } });
Swagger UI 會有兩個文件:v1 和 v2。
GET /api/v1/todo → 只回傳 Title GET /api/v2/todo → 回傳 Title + IsDone
🔹 NSwag 來源:由 Rico Suter 開發,功能比 Swashbuckle 更廣。NuGet 套件:NSwag.AspNetCore
主要用途:
跟 Swashbuckle 一樣能產生 OpenAPI 文件和 Swagger UI
額外支援 API Client 產生器:可以根據 OpenAPI 產生 C# Client 或 TypeScript Client
可整合到 MSBuild / CLI / NPM,適合 CI/CD
特色:
不只是文件,還能做 API SDK 生成,適合前後端分離專案,前端可直接用 TypeScript Client SDK 呼叫 API,有 GUI 工具 NSwagStudio,方便點選操作 如果專案需要「自動產生 API 客戶端」(C# 或 TypeScript),NSwag 更適合。
API SDK 生成的好處
不用手刻 API 呼叫程式碼 → 減少重複工作。
型別安全 (Type-safe) → DTO 定義自動同步,避免「後端改了,前端還在用舊格式」。
開發更快 → API 文件就是程式碼的來源,契約一致。
支援多語言 → 一份 OpenAPI,可以生成 TypeScript、C#、Java、Python SDK。
如果你有 API 的 OpenAPI 文件 (swagger.json),就能用 NSwag 或 OpenAPI Generator 自動產生 SDK。 ✅ 生成後的 TypeScript Client 範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 export class TodoClient { constructor (private baseUrl: string ) {} async getTodos (): Promise <TodoItem []> { const response = await fetch (`${this .baseUrl} /api/todos` ); return response.json (); } async addTodo (item : CreateTodoDto ): Promise <TodoItem > { const response = await fetch (`${this .baseUrl} /api/todos` , { method : "POST" , headers : { "Content-Type" : "application/json" }, body : JSON .stringify (item), }); return response.json (); } }
前端呼叫就很乾淨:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const client = new TodoClient("https://api.example.com" );const todos = await client.getTodos();✅ 生成後的 C# Client 範例 public class TodoClient { private readonly HttpClient _http; public TodoClient (HttpClient http ) { _http = http; } public async Task<ICollection<TodoItem>> GetTodosAsync() { var response = await _http.GetAsync("/api/todos" ); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync<ICollection<TodoItem>>(); } public async Task<TodoItem> AddTodoAsync (CreateTodoDto dto ) { var response = await _http.PostAsJsonAsync("/api/todos" , dto); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync<TodoItem>(); } } var client = new TodoClient(httpClient);var todos = await client.GetTodosAsync();
🌿 保護 Swagger UI 呼叫 MapSwagger().RequireAuthorization 來保護 Swagger UI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 using System.Security.Claims;var builder = WebApplication.CreateBuilder(args);builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddAuthorization(); builder.Services.AddAuthentication("Bearer" ).AddJwtBearer(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); var summaries = new []{ "Freezing" , "Bracing" , "Chilly" , "Cool" , "Mild" , "Warm" , "Balmy" , "Hot" , "Sweltering" , "Scorching" }; app.MapSwagger().RequireAuthorization(); app.MapGet("/" , () => "Hello, World!" ); app.MapGet("/secret" , (ClaimsPrincipal user) => $"Hello {user.Identity?.Name} . My secret" ) .RequireAuthorization(); app.MapGet("/weatherforecast" , () => { var forecast = 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(); return forecast; }) .WithName("GetWeatherForecast" ) .WithOpenApi(); app.Run(); internal record WeatherForecast (DateOnly Date, int TemperatureC, string ? Summary ){ public int TemperatureF => 32 + (int )(TemperatureC / 0.5556 ); }
在上述程式碼中,/weatherforecast 端點不需要授權,但 Swagger 端點需要。
1 curl -i -H "Authorization: Bearer {token}" https://localhost:{port}/swagger/v1/swagger.json
🌿 結語 把 Swagger 想像成一個在你身邊的「API 小精靈」✨。它會默默觀察你的 Controllers,幫你生成一份 OpenAPI 文件;它會把這份文件變成一個漂亮的 UI,讓前後端、QA、PM 甚至客戶,都能一眼看懂;如果你需要,它還能幫你生出前端或後端的 SDK,讓大家不用再重複造輪子。