前言
這是StarBlog系列在2023年的第一篇更新?~
在之前的文章裡,我們已經完成了部分介面的開發,接下來需要使用 curl、Postman 這類工具對這些介面進行測試,但介面一多,每次測試都要一個個填入地址和對應引數會比較麻煩…
我們需要一種直觀的方式來彙總專案裡的所有介面,並且如果能直接在裡面除錯介面,那就更好了。
Swagger:誒嘿,說的不就是我嗎??
Swagger介紹
來一段官網的介紹
Simplify API development for users, teams, and enterprises with the Swagger open source and professional toolset.
翻譯:Swagger 是開源和專業的工具集,可以簡化使用者、團隊和企業的 API 開發。
一般來說,swagger用起來有兩部分,一個是 OpenAPI 一個是 SwaggerUI
在Swagger官網上,OpenAPI 介紹得天花亂墜?
The OpenAPI Specification, formerly known as the Swagger Specification, is the world’s standard for defining RESTful interfaces. The OAS enables developers to design a technology-agnostic API interface that forms the basis of their API development and consumption.
翻譯:OpenAPI 規範,以前稱為 Swagger 規範,是定義 RESTful 介面的世界標準。 OAS 使開發人員能夠設計一個與技術無關的 API 介面,該介面構成了他們 API 開發和使用的基礎。
簡單說 OpenAPI 是個標準,需要每種語言和框架自行實現一個工具,用來把專案裡的介面都整合起來,生成 swagger.json
檔案
然後 SwaggerUI 就是個網頁,讀取這個 swagger.json
就可以把所有介面以及引數顯示出來,還可以很方便除錯,效果如圖。
Swashbuckle.AspNetCore
前面說到每種框架都要自己實現一個工具來生成 swagger.json
,這個 Swashbuckle.AspNetCore 就是 .NetCore 平臺的實現,用就完事了。
專案主頁: https://github.com/domaindrivendev/Swashbuckle.AspNetCore
Tips:如果是建立 WebApi 專案,程式碼模板裡面預設就有 Swagger 了,不用手動新增。
StarBlog專案一開始是使用MVC模板,所以沒有自帶Swagger,需要手動新增。
直接使用nuget新增 Swashbuckle.AspNetCore
這個包就完事了。
這個包功能很多,內建了 SwaggerUI 這個官方介面,還有一個 ReDoc 的純靜態介面文件網頁(這個 ReDoc 只能看介面不能除錯)。
初步使用
為了保證 Program.cs
程式碼整潔,我們在 StarBlog.Web/Extensions
裡面建立 ConfigureSwagger
類
public static class ConfigureSwagger {
public static void AddSwagger(this IServiceCollection services) {
services.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "APIs"});
// 在介面文件上顯示 XML 註釋
var filePath = Path.Combine(System.AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml");
options.IncludeXmlComments(filePath, true);
});
}
public static void UseSwaggerPkg(this IApplicationBuilder app) {
app.UseSwagger();
app.UseSwaggerUI(options => {
options.RoutePrefix = "api-docs/swagger";
options.SwaggerEndpoint("/swagger/v1/swagger.json", "APIs");
});
app.UseReDoc(options => {
options.RoutePrefix = "api-docs/redoc";
options.SpecUrl = "/swagger/v1/swagger.json";
});
}
}
上面程式碼可以看到有三步
AddSwaggerGen
- 對應前文說的生成swagger.json
UseSwagger
- 讓瀏覽器可以訪問到/swagger/v1/swagger.json
這類路徑UseSwaggerUI
- 提供 SwaggerUI 的網頁訪問
然後回到 Program.cs
裡面,分別註冊服務和新增中介軟體就好了。
// 註冊服務
builder.Services.AddSwagger();
// 新增中介軟體
app.UseSwaggerPkg();
現在啟動專案,訪問 http://[本地地址]/api-docs/swagger
就能看到介面文件了
效果大概這樣
擴充套件:關於XML註釋
C# 的程式碼註釋可以匯出XML,然後顯示在 swagger 文件上
注意需要手動在 .csproj
專案配置裡面開啟,才會輸出XML文件
<!-- 輸出XML -->
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
但是開啟XML之後,IDE很蠢的要求我們所有public成員都寫上註釋,很煩,加上 <NoWarn>$(NoWarn);1591</NoWarn>
這行就可以關掉這個警告。
在 Swagger 里載入XML文件,既可以用本文前面寫的方式
var filePath = Path.Combine(System.AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml");
options.IncludeXmlComments(filePath, true);
還可以用第二種,載入目錄裡的全部XML
var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml");
foreach (var file in xmlFiles) {
options.IncludeXmlComments(file, true);
}
具體用哪種,都行吧,看心情~
擴充套件:關於 AddEndpointsApiExplorer
在 AddSwagger
擴充套件方法這裡可能有同學會有疑問
為啥建立 .Net6 專案後預設是這兩行程式碼
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
而我這裡只有一行程式碼
services.AddSwaggerGen();
先說結論:AddEndpointsApiExplorer
是為了支援 Minimal Api 的。
因為 StarBlog 專案使用的是MVC模板,在 Program.cs
的最開始可以看到這行程式碼,新增控制器和檢視
builder.Services.AddControllersWithViews();
翻一下這個框架的原始碼,可以看到這個方法的套娃是這樣的
AddControllersWithViews() -> AddControllersWithViewsCore() -> AddControllersCore()
而在 AddControllersCore
裡面,又呼叫了 AddApiExplorer
private static IMvcCoreBuilder AddControllersCore(IServiceCollection services) {
// This method excludes all of the view-related services by default.
var builder = services
.AddMvcCore()
.AddApiExplorer()
.AddAuthorization()
.AddCors()
.AddDataAnnotations()
.AddFormatterMappings();
if (MetadataUpdater.IsSupported) {
services.TryAddEnumerable(
ServiceDescriptor.Singleton<IActionDescriptorChangeProvider, HotReloadService>());
}
return builder;
}
就是說正常的專案已經有 ApiExplorer
這個東西了,但是 Minimal Api 專案沒有,所以本專案不需要 builder.Services.AddEndpointsApiExplorer();
這行程式碼。
詳情可以閱讀參考資料的第一個連結。
介面分組
介面文件有了,但專案裡介面太多了,幾十個介面全擠在一個頁面上,找都找得眼花了?
這時候可以給介面分個組
先來給 StarBlog 專案裡面的介面分個類,根據不同用途,大致分成這五類:
- admin - 管理員相關介面
- common - 通用公共介面
- auth - 授權介面
- blog - 部落格管理介面
- test - 測試介面
還是在上面那個 ConfigureSwagger.cs
檔案
修改 AddSwagger
方法,把這幾個分組新增進去
services.AddSwaggerGen(options => {
options.SwaggerDoc("admin", new OpenApiInfo {
Version = "v1",
Title = "Admin APIs",
Description = "管理員相關介面"
});
options.SwaggerDoc("common", new OpenApiInfo {
Version = "v1",
Title = "Common APIs",
Description = "通用公共介面"
});
options.SwaggerDoc("auth", new OpenApiInfo {
Version = "v1",
Title = "Auth APIs",
Description = "授權介面"
});
options.SwaggerDoc("blog", new OpenApiInfo {
Version = "v1",
Title = "Blog APIs",
Description = "部落格管理介面"
});
options.SwaggerDoc("test", new OpenApiInfo {
Version = "v1",
Title = "Test APIs",
Description = "測試介面"
});
});
這樣就會生成五個 swagger.json
檔案,路徑分別是
/swagger/admin/swagger.json
/swagger/common/swagger.json
/swagger/auth/swagger.json
/swagger/blog/swagger.json
/swagger/test/swagger.json
所以下面的 UseSwaggerPkg
方法也要對應修改
public static void UseSwaggerPkg(this IApplicationBuilder app) {
app.UseSwagger();
app.UseSwaggerUI(options => {
options.RoutePrefix = "api-docs/swagger";
options.SwaggerEndpoint("/swagger/admin/swagger.json", "Admin");
options.SwaggerEndpoint("/swagger/blog/swagger.json", "Blog");
options.SwaggerEndpoint("/swagger/auth/swagger.json", "Auth");
options.SwaggerEndpoint("/swagger/common/swagger.json", "Common");
options.SwaggerEndpoint("/swagger/test/swagger.json", "Test");
});
}
接下來,要讓 Swagger 知道每個介面都是屬於哪個分組的。
具體方法是在 Controller 上新增 ApiExplorerSettings
特性。
比如 BlogController
是屬於 blog 分組,在 class 定義前面新增一行程式碼
[ApiExplorerSettings(GroupName = "blog")]
public class BlogController : ControllerBase {
// ...
}
其他的 Controller 也是類似的操作,具體分組跟 StarBlog.Web/Apis
下的目錄結構一樣,這裡就不贅述了。
實現效果
做完之後,開啟 swagger 介面文件頁面
可以看到右上角可以選擇介面分組了
搞定。
最佳化分組
前文對於 Swagger 分組的實現其實是一種硬編碼,不同分組的 Controller 上面需要加上 [ApiExplorerSettings(GroupName = "blog")]
特性,分組名全靠複製貼上,在專案比較小的情況下還好,如果分組多起來了,有幾百個介面的時候,估計人就麻了吧?
Q:“你剛才幹嘛不早說?”
A:“循序漸進嘛?”
A:“StarBlog專案也是最近才換到新版分組的?”
在 StarBlog.Web/Models
裡新增個新的類 SwaggerGroup
public class SwaggerGroup {
/// <summary>
/// 組名稱(同時用於做URL字首)
/// </summary>
public string Name { get; set; }
public string? Title { get; set; }
public string? Description { get; set; }
public SwaggerGroup(string name, string? title = null, string? description = null) {
Name = name;
Title = title;
Description = description;
}
/// <summary>
/// 生成 <see cref="Microsoft.OpenApi.Models.OpenApiInfo"/>
/// </summary>
public OpenApiInfo ToOpenApiInfo(string version = "1.0") {
var item = new OpenApiInfo();
Title ??= Name;
Description ??= Name;
return new OpenApiInfo { Title = Title, Description = Description, Version = version };
}
}
然後改造一下 StarBlog.Web/Extensions/ConfigureSwagger.cs
在這個檔案裡面新增個新的類,這樣就不會硬編碼了?
public static class ApiGroups {
public const string Admin = "admin";
public const string Auth = "auth";
public const string Common = "common";
public const string Blog = "blog";
public const string Test = "test";
}
在 ConfigureSwagger
裡新增一些程式碼,建立 SwaggerGroup
列表
public static class ConfigureSwagger {
public static readonly List<SwaggerGroup> Groups = new() {
new SwaggerGroup(ApiGroups.Admin, "Admin APIs", "管理員相關介面"),
new SwaggerGroup(ApiGroups.Auth, "Auth APIs", "授權介面"),
new SwaggerGroup(ApiGroups.Common, "Common APIs", "通用公共介面"),
new SwaggerGroup(ApiGroups.Blog, "Blog APIs", "部落格管理介面"),
new SwaggerGroup(ApiGroups.Test, "Test APIs", "測試介面")
};
}
然後把後面的 AddSwagger
方法改成這樣,那一坨東西,現在一行程式碼就代替了?
public static void AddSwagger(this IServiceCollection services) {
services.AddSwaggerGen(options => {
Groups.ForEach(group => options.SwaggerDoc(group.Name, group.ToOpenApiInfo()));
// XML註釋
var filePath = Path.Combine(AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml");
options.IncludeXmlComments(filePath, true);
});
}
接著是 UseSwaggerPkg
方法,簡單?
public static void UseSwaggerPkg(this IApplicationBuilder app) {
app.UseSwagger();
app.UseSwaggerUI(opt => {
opt.RoutePrefix = "api-docs/swagger";
// 分組
Groups.ForEach(group => opt.SwaggerEndpoint($"/swagger/{group.Name}/swagger.json", group.Name));
});
}
Controller裡面也對應修改成這樣
[ApiExplorerSettings(GroupName = ApiGroups.Blog)]
public class BlogController : ControllerBase {
}
完美??
小結
“Swagger之大,一鍋燉不下”
關於Swagger還有其他的用法,但需要一些前置知識,因此本文不會把StarBlog專案中關於Swagger的部分全部介紹完
等把相關的前置知識寫完,再來完善對應的用法~
這也跟StarBlog的開發過程是吻合的?
參考資料
系列文章
- 基於.NetCore開發部落格專案 StarBlog - (1) 為什麼需要自己寫一個部落格?
- 基於.NetCore開發部落格專案 StarBlog - (2) 環境準備和建立專案
- 基於.NetCore開發部落格專案 StarBlog - (3) 模型設計
- 基於.NetCore開發部落格專案 StarBlog - (4) markdown部落格批次匯入
- 基於.NetCore開發部落格專案 StarBlog - (5) 開始搭建Web專案
- 基於.NetCore開發部落格專案 StarBlog - (6) 頁面開發之部落格文章列表
- 基於.NetCore開發部落格專案 StarBlog - (7) 頁面開發之文章詳情頁面
- 基於.NetCore開發部落格專案 StarBlog - (8) 分類層級結構展示
- 基於.NetCore開發部落格專案 StarBlog - (9) 圖片批次匯入
- 基於.NetCore開發部落格專案 StarBlog - (10) 圖片瀑布流
- 基於.NetCore開發部落格專案 StarBlog - (11) 實現訪問統計
- 基於.NetCore開發部落格專案 StarBlog - (12) Razor頁面動態編譯
- 基於.NetCore開發部落格專案 StarBlog - (13) 加入友情連結功能
- 基於.NetCore開發部落格專案 StarBlog - (14) 實現主題切換功能
- 基於.NetCore開發部落格專案 StarBlog - (15) 生成隨機尺寸圖片
- 基於.NetCore開發部落格專案 StarBlog - (16) 一些新功能 (監控/統計/配置/初始化)
- 基於.NetCore開發部落格專案 StarBlog - (17) 自動下載文章裡的外部圖片
- 基於.NetCore開發部落格專案 StarBlog - (18) 實現本地Typora文章打包上傳
- 基於.NetCore開發部落格專案 StarBlog - (19) Markdown渲染方案探索
- 基於.NetCore開發部落格專案 StarBlog - (20) 圖片顯示最佳化
- 基於.NetCore開發部落格專案 StarBlog - (21) 開始開發RESTFul介面
- 基於.NetCore開發部落格專案 StarBlog - (22) 開發部落格文章相關介面
- 基於.NetCore開發部落格專案 StarBlog - (23) 文章列表介面分頁、過濾、搜尋、排序
- 基於.NetCore開發部落格專案 StarBlog - (24) 統一介面資料返回格式
- 基於.NetCore開發部落格專案 StarBlog - (25) 圖片介面與檔案上傳
- 基於.NetCore開發部落格專案 StarBlog - (26) 整合Swagger介面文件