在開始本篇正文以前,解決一個 @瘋瘋過 指出的錯誤,再次感謝指正。html
步驟以下:git
.Domain.Shared
層中的項目引用,添加nuget依賴包Volo.Abp.Identity.Domain.Shared
,可使用命令:Install-Package Volo.Abp.Identity.Domain.Shared
.Domain
層中引用項目.Domain.Shared
,在模塊類中添加依賴typeof(MeowvBlogDomainSharedModule)
.EntityFrameworkCore
層中的引用項目.Domain.Shared
改爲.Domain
。上一篇文章(http://www.javashuo.com/article/p-rtnsqrfg-kp.html)完成了對API返回模型的封裝,緊接着我打算繼續來折騰一下Swagger,以前的文章中已經簡單用起了Swagger,本篇仍是圍繞它讓其發揮更高的更多的價值。github
當咱們的項目不斷壯大,API持續增多,這時若是想要快速準肯定位到某個API可能不是那麼容易,須要翻半天才能找對咱們的API。因而對Swagger API文檔分組和詳細的文檔描述就有必要了,就本項目而言,博客系統能夠分組爲:博客前臺接口、博客後臺接口、其它公共接口、JWT認證受權接口。json
其中,博客後臺組中的接口須要受權後才能夠調用,須要受權那麼就涉及到身份驗證,在這裏準備採用JWT(JSON WEB TOKEN)的方式進行。api
對Swagger進行分組很簡單,在.Swagger
層中的擴展方法AddSwagger(this IServiceCollection services)
中屢次調用options.SwaggerDoc(...)
便可,像這樣app
... options.SwaggerDoc("v1", new OpenApiInfo { Version = "1.0.0", Title = "個人接口啊1", Description = "接口描述1" }); options.SwaggerDoc("v2", new OpenApiInfo { Version = "1.0.0", Title = "個人接口啊2", Description = "接口描述2" }); ... ...
不過這樣顯得有點low,而後能夠轉變一下思路使用遍歷的方式進行。options.SwaggerDoc(...)
接收兩個參數:string name, OpenApiInfo info
。ui
name
:能夠理解爲當前分組的前綴;OpenApiInfo
:有許多可配置的參數,在這裏我只用到三個,Version
、Title
、Description
。this
要注意,當在AddSwagger(...)
中調用完後,還須要在咱們的擴展方法UseSwaggerUI(this IApplicationBuilder app)
中options.SwaggerEndpoint()
使用它,一樣的也用遍歷的方法。它接收的的參數:string url, string name
。url
url
:這裏的url
要與前面配置的name
參數對應。spa
name
:咱們自定義顯示的分組名稱。
因而能夠直接在擴展方法中新建一個內部類:SwaggerApiInfo
internal class SwaggerApiInfo { /// <summary> /// URL前綴 /// </summary> public string UrlPrefix { get; set; } /// <summary> /// 名稱 /// </summary> public string Name { get; set; } /// <summary> /// <see cref="Microsoft.OpenApi.Models.OpenApiInfo"/> /// </summary> public OpenApiInfo OpenApiInfo { get; set; } }
而後新建一個List<SwaggerApiInfo>
手動爲其初始化一些值。
... /// <summary> /// Swagger分組信息,將進行遍歷使用 /// </summary> private static readonly List<SwaggerApiInfo> ApiInfos = new List<SwaggerApiInfo>() { new SwaggerApiInfo { UrlPrefix = Grouping.GroupName_v1, Name = "博客前臺接口", OpenApiInfo = new OpenApiInfo { Version = version, Title = "阿星Plus - 博客前臺接口", Description = description } }, new SwaggerApiInfo { UrlPrefix = Grouping.GroupName_v2, Name = "博客後臺接口", OpenApiInfo = new OpenApiInfo { Version = version, Title = "阿星Plus - 博客後臺接口", Description = description } }, new SwaggerApiInfo { UrlPrefix = Grouping.GroupName_v3, Name = "通用公共接口", OpenApiInfo = new OpenApiInfo { Version = version, Title = "阿星Plus - 通用公共接口", Description = description } }, new SwaggerApiInfo { UrlPrefix = Grouping.GroupName_v4, Name = "JWT受權接口", OpenApiInfo = new OpenApiInfo { Version = version, Title = "阿星Plus - JWT受權接口", Description = description } } }; ...
version
:咱們將其配置在appsettings.json
中,作到動態能夠修改。
//AppSettings.cs ... /// <summary> /// ApiVersion /// </summary> public static string ApiVersion => _config["ApiVersion"]; ... //appsettings.json { ... "ApiVersion": "1.0.0" ... }
description
:由於屢次使用,就定義一個變量,內容自擬主要是一些介紹性的描述,將在Swagger界面進行顯示。
UrlPrefix
:分別爲,v1,v2,v3,v4。在Domain.Shared
層中爲其定義好常量
//MeowvBlogConsts.cs ... /// <summary> /// 分組 /// </summary> public static class Grouping { /// <summary> /// 博客前臺接口組 /// </summary> public const string GroupName_v1 = "v1"; /// <summary> /// 博客後臺接口組 /// </summary> public const string GroupName_v2 = "v2"; /// <summary> /// 其餘通用接口組 /// </summary> public const string GroupName_v3 = "v3"; /// <summary> /// JWT受權接口組 /// </summary> public const string GroupName_v4 = "v4"; } ...
如今修改擴展方法AddSwagger(...)
,遍歷List<SwaggerApiInfo>
。
... public static IServiceCollection AddSwagger(this IServiceCollection services) { return services.AddSwaggerGen(options => { //options.SwaggerDoc("v1", new OpenApiInfo //{ // Version = "1.0.0", // Title = "個人接口啊", // Description = "接口描述" //}); // 遍歷並應用Swagger分組信息 ApiInfos.ForEach(x => { options.SwaggerDoc(x.UrlPrefix, x.OpenApiInfo); }); ... }); } ...
在擴展方法UseSwaggerUI(...)
使用,通用也須要遍歷。
... // 遍歷分組信息,生成Json ApiInfos.ForEach(x => { options.SwaggerEndpoint($"/swagger/{x.UrlPrefix}/swagger.json", x.Name); }); ...
細心的同窗能夠發現,咱們前幾篇文章打開Swagger文檔的時候都是須要手動更改URL地址:.../swagger
才能正確進入,其實Swagger是支持配置路由的。同時我們也將頁面Title也給改了吧。看下面UseSwaggerUI(...)
完整代碼:
... /// <summary> /// UseSwaggerUI /// </summary> /// <param name="app"></param> public static void UseSwaggerUI(this IApplicationBuilder app) { app.UseSwaggerUI(options => { // 遍歷分組信息,生成Json ApiInfos.ForEach(x => { options.SwaggerEndpoint($"/swagger/{x.UrlPrefix}/swagger.json", x.Name); }); // 模型的默認擴展深度,設置爲 -1 徹底隱藏模型 options.DefaultModelsExpandDepth(-1); // API文檔僅展開標記 options.DocExpansion(DocExpansion.List); // API前綴設置爲空 options.RoutePrefix = string.Empty; // API頁面Title options.DocumentTitle = "😍接口文檔 - 阿星Plus⭐⭐⭐"; }); } ...
options.DefaultModelsExpandDepth(-1);
是模型的默認擴展深度,設置爲 -1 徹底隱藏模型。
options.DocExpansion(DocExpansion.List);
表明API文檔僅展開標記,不默然展開全部接口,須要咱們手動去點擊才展開,能夠自行查看DocExpansion
。
options.RoutePrefix = string.Empty;
表明路由設置爲空,直接打開頁面就能夠訪問了。
options.DocumentTitle = "😍接口文檔 - 阿星Plus⭐⭐⭐";
是設置文檔頁面的標題的。
完成以上操做,在Controller中使用 Attribute:[ApiExplorerSettings(GroupName = ...)]
指定是哪一個分組而後就能夠愉快的使用了。
默認不指定的話就是所有都有,目前只有兩個Controller,咱們將HelloWorldController
設置成v3,BlogController
設置成v1。
//HelloWorldController.cs ... [ApiExplorerSettings(GroupName = Grouping.GroupName_v3)] public class HelloWorldController : AbpController { ... } ... //BlogController.cs ... [ApiExplorerSettings(GroupName = Grouping.GroupName_v1)] public class BlogController : AbpController { ... } ...
編譯運行,打開咱們的Swagger文檔看一下。
本身試着換切換一下分組試試吧,大功告成。
在Swagger文檔中,默認只顯示咱們的Controller的名稱,其實他也是支持描述信息的,這是就須要咱們自行擴展了。在.Swagger
層新建一個文件夾Filters,添加SwaggerDocumentFilter
類來實現IDocumentFilter接口。
//SwaggerDocumentFilter.cs using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Collections.Generic; using System.Linq; namespace Meowv.Blog.Swagger.Filters { /// <summary> /// 對應Controller的API文檔描述信息 /// </summary> public class SwaggerDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { var tags = new List<OpenApiTag> { new OpenApiTag { Name = "Blog", Description = "我的博客相關接口", ExternalDocs = new OpenApiExternalDocs { Description = "包含:文章/標籤/分類/友鏈" } } new OpenApiTag { Name = "HelloWorld", Description = "通用公共接口", ExternalDocs = new OpenApiExternalDocs { Description = "這裏是一些通用的公共接口" } } }; // 按照Name升序排序 swaggerDoc.Tags = tags.OrderBy(x => x.Name).ToList(); } } }
實現Apply(...)
方法後,使用Linq語法對文檔排個序,而後最重要的使用這個Filter,在擴展方法AddSwagger(...)
中使用
public static IServiceCollection AddSwagger(this IServiceCollection services) { return services.AddSwaggerGen(options => { ... // 應用Controller的API文檔描述信息 options.DocumentFilter<SwaggerDocumentFilter>(); }); }
再打開Swagger文檔看看效果。
ok,此時描述信息也出來了。
在Swagger文檔中開啓小綠鎖是很是簡單的,只需添加一個包:Swashbuckle.AspNetCore.Filters
,直接使用命令安裝:Install-Package Swashbuckle.AspNetCore.Filters
而後再擴展方法AddSwagger(this IServiceCollection services)
中調用
public static IServiceCollection AddSwagger(this IServiceCollection services) { return services.AddSwaggerGen(options => { ... var security = new OpenApiSecurityScheme { Description = "JWT模式受權,請輸入 Bearer {Token} 進行身份驗證", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey }; options.AddSecurityDefinition("oauth2", security); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { security, new List<string>() } }); options.OperationFilter<AddResponseHeadersFilter>(); options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>(); options.OperationFilter<SecurityRequirementsOperationFilter>(); ... }); }
以上便實現了在Swagger文檔中顯示小綠鎖,咱們new的OpenApiSecurityScheme
對象,具體參數你們能夠自行看一下注釋就明白具體含義。分別調用options.AddSecurityDefinition(...)
、options.AddSecurityRequiremen(...)
、options.OperationFilter(...)
,編譯運行,打開瞅瞅。
如今只是作了小綠鎖的顯示,可是並無實際意義,由於在.net core中還須要配置咱們的身份認證受權代碼,才能具體發揮其真正的做用,因此目前咱們的api仍是處於裸奔狀態,誰都能調用你的api,等你發現你寫的文章都被別人刪了,你都不知道爲何。
實現JWT,將在下篇文章中詳細說明,本篇到這裏就結束了,咱們完善了Swagger文檔,給接口加了分組、描述,還有小綠鎖。老鐵,你學會了嗎?😁😁😁