基於 abp vNext 和 .NET Core 開發部落格專案 - 再說Swagger,分組、描述、小綠鎖

阿星Plus發表於2020-05-22

在開始本篇正文之前,解決一個 @瘋瘋過 指出的錯誤,再次感謝指正。

0

步驟如下:

  • 刪掉.Domain.Shared層中的專案引用,新增nuget依賴包Volo.Abp.Identity.Domain.Shared,可以使用命令:Install-Package Volo.Abp.Identity.Domain.Shared
  • .Domain層中引用專案.Domain.Shared,在模組類中新增依賴typeof(MeowvBlogDomainSharedModule)
  • .EntityFrameworkCore層中的引用專案.Domain.Shared改成.Domain

0


上一篇文章(https://www.cnblogs.com/meowv/p/12924409.html)完成了對API返回模型的封裝,緊接著我打算繼續來折騰一下Swagger,之前的文章中已經簡單用起了Swagger,本篇還是圍繞它讓其發揮更高的更多的價值。

當我們的專案不斷壯大,API持續增多,這時如果想要快速準確定位到某個API可能不是那麼容易,需要翻半天才能找對我們的API。於是對Swagger API文件分組和詳細的文件描述就有必要了,就本專案而言,部落格系統可以分組為:部落格前臺介面、部落格後臺介面、其它公共介面、JWT認證授權介面。

其中,部落格後臺組中的介面需要授權後才可以呼叫,需要授權那麼就涉及到身份驗證,在這裡準備採用JWT(JSON WEB TOKEN)的方式進行。

分組

對Swagger進行分組很簡單,在.Swagger層中的擴充套件方法AddSwagger(this IServiceCollection services)中多次呼叫options.SwaggerDoc(...)即可,像這樣

...
    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

name:可以理解為當前分組的字首;OpenApiInfo:有許多可配置的引數,在這裡我只用到三個,VersionTitleDescription

要注意,當在AddSwagger(...)中呼叫完後,還需要在我們的擴充套件方法UseSwaggerUI(this IApplicationBuilder app)options.SwaggerEndpoint()使用它,同樣的也用遍歷的方法。它接收的的引數:string url, string name

url:這裡的url要與前面配置的name引數對應。

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文件看一下。

1

2

自己試著換切換一下分組試試吧,大功告成。​

描述

在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文件看看效果。

3

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(...),編譯執行,開啟瞅瞅。

4

現在只是做了小綠鎖的顯示,但是並沒有實際意義,因為在.net core中還需要配置我們的身份認證授權程式碼,才能具體發揮其真正的作用,所以目前我們的api還是處於裸奔狀態,誰都能呼叫你的api,等你發現你寫的文章都被別人刪了,你都不知道為什麼。

實現JWT,將在下篇文章中詳細說明,本篇到這裡就結束了,我們完善了Swagger文件,給介面加了分組、描述,還有小綠鎖。老鐵,你學會了嗎????

開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

相關文章