1. 程式人生 > 實用技巧 >.net core的Swagger介面文件使用教程(二):NSwag

.net core的Swagger介面文件使用教程(二):NSwag

  上一篇介紹了Swashbuckle ,地址:.net core的Swagger介面文件使用教程(一):Swashbuckle

  講的東西還挺多,怎奈微軟還推薦了一個NSwag,那就繼續寫吧!

  但是和Swashbuckle一樣,如果還是按照那樣寫,東西有點多了,所以這裡就偷個懶吧,和Swashbuckle對照的去寫,介紹一些常用的東西算了,所以建議看完上一篇再繼續這裡。

  

  一、一般用法

  建立一個.net core專案(這裡採用的是.net core3.1),然後使用nuget安裝NSwag.AspNetCore,建議安裝最新版本。

  同樣的,假如有一個介面:  

    ///
<summary> /// 測試介面 /// </summary> [ApiController] [Route("[controller]")] public class HomeController : ControllerBase { /// <summary> /// Hello World /// </summary> /// <returns>輸出Hello World</returns> [HttpGet]
public string Get() { return "Hello World"; } }

  介面修改Startup,在ConfigureServices和Configure方法中新增服務和中介軟體  

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOpenApiDocument(settings =>
        {
            settings.DocumentName 
= "v1"; settings.Version = "v0.0.1"; settings.Title = "測試介面專案"; settings.Description = "介面文件說明"; }); ... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
      ... app.UseOpenApi(); app.UseSwaggerUi3(); ... }

  然後執行專案,輸入http://localhost:5000/swagger,得到介面文件頁面:

  

  點選Try it out可以直接呼叫介面。

  同樣的,這裡的介面沒有註解,不太友好,可以和Swashbuckle一樣生成xml註釋檔案載入:

  右鍵專案=》切換到生成(Build),在最下面輸出輸出中勾選【XML文件檔案】,同時,在錯誤警告的取消顯示警告中新增1591程式碼:

  

  不過,與Swashbuckle不一樣的是,Swashbuckle需要使用IncludeXmlComments方法載入註釋檔案,如果註釋檔案不存在,IncludeXmlComments方法還會丟擲異常,但是NSwag不需要手動載入,預設xml註釋檔案和它對應點dll應該放在同一目錄且同名才能完成載入!

  按照上面的操作,執行專案後,介面就有註解了:

  

  但是控制器標籤欄還是沒有註解,這是因為NSwag的控制器標籤預設從OpenApiTagAttribute中讀取   

    [OpenApiTag("測試標籤",Description = "測試介面")]
    public class HomeController : ControllerBase

  執行後顯示:

  

  其實還可以修改這個預設行為,settings有一個UseControllerSummaryAsTagDescription屬性,將它設定成 true就可以從xml註釋檔案中載入描述了:  

    services.AddOpenApiDocument(settings =>
    {
        ...

        //可以設定從註釋檔案載入,但是載入的內容可被OpenApiTagAttribute特性覆蓋
        settings.UseControllerSummaryAsTagDescription = true;
    });

  執行後顯示:

  

  接著是認證,比如JwtBearer認證,這個和Swashbuckle是類似的,只不過拓展方法換成了AddSecurity:  

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOpenApiDocument(settings =>
        {
            settings.DocumentName = "v1";
            settings.Version = "v0.0.1";
            settings.Title = "測試介面專案";
            settings.Description = "介面文件說明";

            //可以設定從註釋檔案載入,但是載入的內容可悲OpenApiTagAttribute特性覆蓋
            settings.UseControllerSummaryAsTagDescription = true;

            //定義JwtBearer認證方式一
            settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
            {
                Description = "這是方式一(直接在輸入框中輸入認證資訊,不需要在開頭新增Bearer)",
                Name = "Authorization",//jwt預設的引數名稱
                In = OpenApiSecurityApiKeyLocation.Header,//jwt預設存放Authorization資訊的位置(請求頭中)
                Type = OpenApiSecuritySchemeType.Http,
                Scheme = "bearer"
            });

            //定義JwtBearer認證方式二
            settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
            {
                Description = "這是方式二(JWT授權(資料將在請求頭中進行傳輸) 直接在下框中輸入Bearer {token}(注意兩者之間是一個空格))",
                Name = "Authorization",//jwt預設的引數名稱
                In = OpenApiSecurityApiKeyLocation.Header,//jwt預設存放Authorization資訊的位置(請求頭中)
                Type = OpenApiSecuritySchemeType.ApiKey
            });
        });

        ...
    }

  到這裡,就是NSwag的一般用法了,可以滿足一般的需求了。

  二、服務注入(AddOpenApiDocument和AddSwaggerDocument)

  NSwag注入服務有兩個方法:AddOpenApiDocument和AddSwaggerDocument,兩者的區別就是架構型別不一樣,AddOpenApiDocument的SchemaType使用的是OpenApi3,AddSwaggerDocument的SchemaType使用的是Swagger2:  

    /// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
    /// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
    /// <param name="configure">Configure the document.</param>
    public static IServiceCollection AddOpenApiDocument(this IServiceCollection serviceCollection, Action<AspNetCoreOpenApiDocumentGeneratorSettings, IServiceProvider> configure = null)
    {
        return AddSwaggerDocument(serviceCollection, (settings, services) =>
        {
            settings.SchemaType = SchemaType.OpenApi3;
            configure?.Invoke(settings, services);
        });
    }
    /// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
    /// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
    /// <param name="configure">Configure the document.</param>
    public static IServiceCollection AddSwaggerDocument(this IServiceCollection serviceCollection, Action<AspNetCoreOpenApiDocumentGeneratorSettings, IServiceProvider> configure = null)
    {
        serviceCollection.AddSingleton(services =>
        {
            var settings = new AspNetCoreOpenApiDocumentGeneratorSettings
            {
                SchemaType = SchemaType.Swagger2,
            };

            configure?.Invoke(settings, services);

            ...
        });

        ...
    }

  個人推薦使用AddOpenApiDocument。  

    services.AddOpenApiDocument(settings =>
    {
        //新增程式碼
    });

  同樣的,無論是AddOpenApiDocument還是AddSwaggerDocument,最終都是依賴AspNetCoreOpenApiDocumentGeneratorSettings來完成,與Swashbuckle不同的是,AddOpenApiDocument方法每次呼叫只會生成一個swagger介面文件物件,從上面的例子也能看出來:

  DocumentName

  介面文件名,也就是Swashbuckle中SwaggerDoc方法中的name引數。

  Version

  介面文件版本,也就是Swashbuckle中SwaggerDoc方法中的第二個OpenApiInfo的Version屬性。

  Title

  介面專案名稱,也就是Swashbuckle中SwaggerDoc方法中的第二個OpenApiInfo的Title屬性。

  Description

  介面專案介紹,也就是Swashbuckle中SwaggerDoc方法中的第二個OpenApiInfo的Description屬性。

  PostProcess

  這個是一個委託,在生成SwaggerDocument之後執行,需要注意的是,因為NSwag有快取機制的存在PostProcess可能只會執行一遍

  比如:因為NSwag沒有直接提供Swashbuckle中SwaggerDoc方法中的第二個OpenApiInfo的Contact屬性的配置,這時我們可以使用PostProcess實現。  

    settings.PostProcess = document =>
    {
        document.Info.Contact = new OpenApiContact()
        {
            Name = "zhangsan",
            Email = "[email protected]",
            Url = null
        };
    };

  ApiGroupNames

  無論是Swashbuckle還是NSwag都支援生成多個介面文件,但是在介面與文件歸屬上不一致:

  在Swashbuckle中,通過ApiExplorerSettingsAttribute特性的GroupName屬性指定documentName來實現的,而NSwag雖然也是用ApiExplorerSettingsAttribute特性實現,但是此時的GroupName不在是documentName,而是ApiGroupNames屬性指定的元素值了:

  比如下面三個介面:  

    /// <summary>
    /// 未使用ApiExplorerSettings特性,表名屬於每一個swagger文件
    /// </summary>
    /// <returns>結果</returns>
    [HttpGet("All"), Authorize]
    public string All()
    {
        return "All";
    }
    /// <summary>
    /// 使用ApiExplorerSettings特性表名該介面屬於swagger文件v1
    /// </summary>
    /// <returns>Get結果</returns>
    [HttpGet]
    [ApiExplorerSettings(GroupName = "demo1")]
    public string Get()
    {
        return "Get";
    }
    /// <summary>
    /// 使用ApiExplorerSettings特性表名該介面屬於swagger文件v2
    /// </summary>
    /// <returns>Post結果</returns>
    [HttpPost]
    [ApiExplorerSettings(GroupName = "demo2")]
    public string Post()
    {
        return "Post";
    }

  定義兩個文件:  

    services.AddOpenApiDocument(settings =>
    {
        settings.DocumentName = "v1";
        settings.Version = "v0.0.1";
        settings.Title = "測試介面專案";
        settings.Description = "介面文件說明";
        settings.ApiGroupNames = new string[] { "demo1" };

        settings.PostProcess = document =>
        {
            document.Info.Contact = new OpenApiContact()
            {
                Name = "zhangsan",
                Email = "[email protected]",
                Url = null
            };
        };
    });
    services.AddOpenApiDocument(settings =>
    {
        settings.DocumentName = "v2";
        settings.Version = "v0.0.2";
        settings.Title = "測試介面專案v0.0.2";
        settings.Description = "介面文件說明v0.0.2";
        settings.ApiGroupNames = new string[] { "demo2" };

        settings.PostProcess = document =>
        {
            document.Info.Contact = new OpenApiContact()
            {
                Name = "lisi",
                Email = "[email protected]",
                Url = null
            };
        };
    });

  這時不用像Swashbuckle還要在中介軟體中新增文件地址,NSwag中介軟體會自動根據路由模板和文件生成文件地址資訊,所以直接執行就可以了:

  

  

  可以注意到,All既不屬於v1文件也不屬於v2文件,也就是說,如果設定了ApiGroupNames,那就回嚴格的按ApiGroupNames來比較,只有匹配的GroupName在ApiGroupNames屬性中才算屬於這個介面文件,這也是NSwag和Swashbuckle不同的一點。

  另外,同樣的,NSwag也支援使用IActionModelConvention和IControllerModelConvention設定GroupName,具體可以參考上一篇博文

  UseControllerSummaryAsTagDescription  

  這個屬性上面例子有介紹,因為NSwag的控制器標籤預設從OpenApiTagAttribute中讀取,而不是從註釋文件讀取,將此屬性設定成 true就可以從註釋文件讀取了,但是讀取的內容可被OpenApiTagAttribute特性覆蓋。

  AddSecurity

  AddSecurity拓展方法用於新增認證,它是兩個過載方法:  

    public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, OpenApiSecurityScheme swaggerSecurityScheme);
    public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, IEnumerable<string> globalScopeNames, OpenApiSecurityScheme swaggerSecurityScheme);

  雖然是過載,但是兩個方法的作用差別還挺大,第一個(不帶globalScopeNames引數)的方法的作用類似Swashbuckle的AddSecurityDefinition方法,只是宣告的作用,而第二個(有globalScopeNames引數)的方法作用類似於Swashbuckle的AddSecurityRequirement方法,也就是說,這兩個過載方法,一個僅僅是宣告認證,另一個是除了宣告認證,還會將認證全域性的作用於每個介面,不過這兩個方法的實現是使用DocumentProcessors(類似Swashbuckle的DocumentFilter)來實現的  

    /// <summary>Appends the OAuth2 security scheme and requirement to the document's security definitions.</summary>
    /// <remarks>Adds a <see cref="SecurityDefinitionAppender"/> document processor with the given arguments.</remarks>
    /// <param name="settings">The settings.</param>
    /// <param name="name">The name/key of the security scheme/definition.</param>
    /// <param name="swaggerSecurityScheme">The Swagger security scheme.</param>
    public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, OpenApiSecurityScheme swaggerSecurityScheme)
    {
        settings.DocumentProcessors.Add(new SecurityDefinitionAppender(name, swaggerSecurityScheme));
        return settings;
    }

    /// <summary>Appends the OAuth2 security scheme and requirement to the document's security definitions.</summary>
    /// <remarks>Adds a <see cref="SecurityDefinitionAppender"/> document processor with the given arguments.</remarks>
    /// <param name="settings">The settings.</param>
    /// <param name="name">The name/key of the security scheme/definition.</param>
    /// <param name="globalScopeNames">The global scope names to add to as security requirement with the scheme name in the document's 'security' property (can be an empty list).</param>
    /// <param name="swaggerSecurityScheme">The Swagger security scheme.</param>
    public static OpenApiDocumentGeneratorSettings AddSecurity(this OpenApiDocumentGeneratorSettings settings, string name, IEnumerable<string> globalScopeNames, OpenApiSecurityScheme swaggerSecurityScheme)
    {
        settings.DocumentProcessors.Add(new SecurityDefinitionAppender(name, globalScopeNames, swaggerSecurityScheme));
        return settings;
    }

  而SecurityDefinitionAppender是一個實現了IDocumentProcessor介面的類,它實現的Porcess如下,其中_scopeNames就是上面方法傳進來的globalScopeNames:

    /// <summary>Processes the specified Swagger document.</summary>
    /// <param name="context"></param>
    public void Process(DocumentProcessorContext context)
    {
        context.Document.SecurityDefinitions[_name] = _swaggerSecurityScheme;

        if (_scopeNames != null)
        {
            if (context.Document.Security == null)
            {
                context.Document.Security = new Collection<OpenApiSecurityRequirement>();
            }

            context.Document.Security.Add(new OpenApiSecurityRequirement
            {
                { _name, _scopeNames }
            });
        }
    }

  至於其他用法,可以參考上面的一般用法和上一篇中介紹的Swashbuckle的AddSecurityDefinition方法和AddSecurityRequirement方法的用法,很相似。

  DocumentProcessors

  DocumentProcessors類似於Swashbuckle的DocumentFilter方法,只不過DocumentFilter方法時實現IDocumentFilter介面,而DocumentProcessors一個IDocumentProcessor集合屬性,是需要實現IDocumentProcessor介面然後新增到集合中去。需要注意的是,因為NSwag有快取機制的存在DocumentProcessors可能只會執行一遍

  另外,你可能注意到,上面有介紹過一個PostProcess方法,其實個人覺得PostProcess和DocumentProcessors區別不大,但是DocumentProcessors是在PostProcess之前呼叫執行,原始碼中:  

    public async Task<OpenApiDocument> GenerateAsync(ApiDescriptionGroupCollection apiDescriptionGroups)
    {
        ...

foreach (var processor in Settings.DocumentProcessors) { processor.Process(new DocumentProcessorContext(document, controllerTypes, usedControllerTypes, schemaResolver, Settings.SchemaGenerator, Settings)); } Settings.PostProcess?.Invoke(document); return document; }

  可能是作者覺得DocumentProcessors有點繞,所以提供了一個委託供我們簡單處理吧,用法也可以參考上一篇中的Swashbuckle的DocumentFilter方法,比如全域性的新增認證,全域性的新增Server等等。

  OperationProcessors

  OperationProcessors類似Swashbuckle的OperationFilter方法,只不過OperationFilter實現的是IOperationFilter,而OperationProcessors是IOperationProcessor介面集合。需要注意的是,因為NSwag有快取機制的存在OperationProcessors可能只會執行一遍

  同樣的,可能作者為了方便我們使用,已經定義好了一個OperationProcessor類,我們可以將我們的邏輯當做引數去例項化OperationProcessor類,然後新增到OperationProcessors集合中即可,不過作者還提供了一個AddOperationFilter方法,可以往OperationProcessors即可開始位置新增過期操作:  

    /// <summary>Inserts a function based operation processor at the beginning of the pipeline to be used to filter operations.</summary>
    /// <param name="filter">The processor filter.</param>
    public void AddOperationFilter(Func<OperationProcessorContext, bool> filter)
    {
        OperationProcessors.Insert(0, new OperationProcessor(filter));
    }

  所以我們可以這麼用:  

    settings.AddOperationFilter(context =>
    {
        //我們的邏輯
        return true;
    });

  另外,因為無論使用AddOperationFilter方法,還是直接往OperationProcessors集合中新增IOperationProcessor物件,都會對所有Action(或者說Operation)進行呼叫,NSwag還有一個SwaggerOperationProcessorAttribute特性,用於指定某些特定Action才會呼叫執行。當然,SwaggerOperationProcessorAttribute的例項化需要指定一個實現了IOperationProcessor介面的型別以及例項化它所需要的的引數。

  與Swashbuckle不同的是,IOperationProcessor的Process介面要求返回一個bool型別的值,表示介面是否要在swaggerUI頁面展示,如果返回false,介面就不會在前端展示了,而且後續的IOperationProcessor物件也不再繼續呼叫執行。  

    private bool RunOperationProcessors(OpenApiDocument document, Type controllerType, MethodInfo methodInfo, OpenApiOperationDescription operationDescription, List<OpenApiOperationDescription> allOperations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver)
    {
        var context = new OperationProcessorContext(document, operationDescription, controllerType,
            methodInfo, swaggerGenerator, Settings.SchemaGenerator, schemaResolver, Settings, allOperations);

        // 1. Run from settings
        foreach (var operationProcessor in Settings.OperationProcessors)
        {
            if (operationProcessor.Process(context)== false)
            {
                return false;
            }
        }

        // 2. Run from class attributes
        var operationProcessorAttribute = methodInfo.DeclaringType.GetTypeInfo()
            .GetCustomAttributes()
        // 3. Run from method attributes
            .Concat(methodInfo.GetCustomAttributes())
            .Where(a => a.GetType().IsAssignableToTypeName("SwaggerOperationProcessorAttribute", TypeNameStyle.Name));

        foreach (dynamic attribute in operationProcessorAttribute)
        {
            var operationProcessor = ObjectExtensions.HasProperty(attribute, "Parameters") ?
                (IOperationProcessor)Activator.CreateInstance(attribute.Type, attribute.Parameters) :
                (IOperationProcessor)Activator.CreateInstance(attribute.Type);

            if (operationProcessor.Process(context) == false)
            {
                return false;
            }
        }

        return true;
    }

  至於其它具體用法,具體用法可以參考上一篇介紹的Swashbuckle的OperationFilter方法,如給特定Operation新增認證,或者對響應介面包裝等等。

  其它配置

  AspNetCoreOpenApiDocumentGeneratorSettings繼承於OpenApiDocumentGeneratorSettings和JsonSchemaGeneratorSettings還有茫茫多的配置,感興趣的自己看原始碼吧,畢竟它和Swashbuckle差不多,一般的需求都能滿足了,實現滿足不了,可以使用DocumentProcessors和OperationProcessors來實現,就跟Swashbuckle的DocumentFilter和OperationFilter一樣。

  但是有些問題可能就不行了,比如虛擬路徑問題,Swashbuckle採用在Server上加路徑來實現,而因為NSwag沒有像Swashbuckle的AddServer方法,想到可以使用上面的PostProcess方法或者使用DocumentProcessors來實現,但是現實是打臉,因為作者的處理方式是,執行PostProcess方法和DocumentProcessors之後,會把OpenAPIDocument上的Servers先清空,然後再加上當前SwaggerUI所在的域名地址,可能作者覺著這樣能滿足大部分人的需求吧。但是作者還是提供了其他的方式來操作,會在後面的中介軟體中介紹

  三、新增Swagger中介軟體(UseOpenApi、UseSwagger和UseSwaggerUi3、UseSwaggerUi)

  UseOpenApi、UseSwagger

  首先UseOpenApi、UseSwagger和Swashbuckle的UseSwagger的作用一樣的,主要用於攔截swagger.json請求,從而可以獲取返回所需的介面架構資訊,不同點在於NSwag的UseOpenApi、UseSwagger具有快取機制,也就是說,如果第一次獲取到了介面文件,會已json格式將文件加入到本地快取中,下次直接從快取獲取,因為快取的存在,所以上面介紹的OperationProcessors和DocumentProcessors都不會再執行了。

  另外,UseSwagger是舊版本,已經不推薦使用了,推薦使用UseOpenApi:  

    app.UseOpenApi(settings =>
    {
        //中介軟體設定
    });

  OpenApiDocumentMiddlewareSettings

  UseOpenApi依賴OpenApiDocumentMiddlewareSettings物件完成配置過程,主要屬性有:

  Path

  Path表示攔截請求的格式,也就是攔截swagger.json的路由格式,這個跟Swashbuckle一樣,因為需要從路由知道是哪個文件,然後才能去找這個文件的所有介面解析返回,它的預設值是/swagger/{documentName}/swagger.json。

  同樣的,因為這個值關係比較重要,儘可能不要去修改吧。

  DocumentName

  從上面的Path引數的預設值中可以看到,其中有個{documentName}引數NSwag並沒有要求Path中必須有{documentName}引數。

  如果沒有這個引數,就必須指定這個屬性DocumentName,只是也就是說NSwag只為一個介面文件服務。

  如果有這個引數,NSwag會遍歷所有定義的介面文件,然後分別對Path屬性替換掉其中中的{documentName}引數,然後分別攔截每個文件獲取架構資訊的swagger.json請求。

  PostProcess

  服務注入部分有一個PostProcess方法,功能其實類似於DocumentProcessors,就是對介面文件做一個調整,而現在這裡又有一個PostProcess方法,它則是根據當前請求來調整介面文件用的。

  比如,上面有介紹,如果在服務注入部分使用PostProcess方法或者DocumentProcessors添加了Server,是沒有效果的,這個是因為NSwag在獲取到文件之後,有意的清理了文件的Servers屬性,然後加上了當前請求的地址:  

    /// <summary>Generates the Swagger specification.</summary>
    /// <param name="context">The context.</param>
    /// <returns>The Swagger specification.</returns>
    protected virtual async Task<OpenApiDocument> GenerateDocumentAsync(HttpContext context)
    {
        var document = await _documentProvider.GenerateAsync(_documentName);

        document.Servers.Clear();
        document.Servers.Add(new OpenApiServer
        {
            Url = context.Request.GetServerUrl()
        });

        _settings.PostProcess?.Invoke(document, context.Request);

        return document;
    }

  注意到上面的原始碼,在清理之後,還呼叫了這個PostProcess委託,因此,我們可以將新增Server部分的程式碼寫到這個PostProcess中:  

    app.UseOpenApi(settings =>
    {
        settings.PostProcess = (document, request) =>
        {
            //清理掉NSwag加上去的
            document.Servers.Clear();
            document.Servers.Add(new OpenApiServer() { Url = "http://localhost:90/NSwag", Description = "地址1" });
            document.Servers.Add(new OpenApiServer() { Url = "http://127.0.0.1:90/NSwag", Description = "地址2" });
            //192.168.28.213是我本地IP
            document.Servers.Add(new OpenApiServer() { Url = "http://192.168.28.213:90/NSwag", Description = "地址3" });
        };
    });

  看來,作者還是很友好的,做了點小動作還提供給我們一個修改的方法。

  CreateDocumentCacheKey

  上面有提到,NSwag的介面文旦有快取機制,第一次獲取之後就會以json格式被快取,接下就會從快取中讀取,而CreateDocumentCacheKey就是快取的鍵值工廠,用於生成快取鍵值用的,如果不設定,那麼快取的鍵值就是string.Empty。

  那可能會問,如果不想用快取呢,不妨設定CreateDocumentCacheKey成這樣:  

    app.UseOpenApi(settings =>
    {
        settings.CreateDocumentCacheKey = request => DateTime.Now.ToString();
    });

  然後你就會發現,過了一段時間之後,你的程式掛了,OutOfMemory!

  所以,好好的用快取的,從原始碼中目前沒發現有什麼辦法可以取消快取,況且使用快取可以提高響應速度,為何不用?如果實在要遮蔽快取,那就是改改原始碼再編譯引用吧。

  ExceptionCacheTime

  既然是程式,那就有可能會丟擲異常,獲取介面文件架構也不例外,而ExceptionCacheTime表示在獲取介面文件發生異常後的一段時間內,使用返回這個異常,ExceptionCacheTime預設是TimeSpan.FromSeconds(10)

  UseSwaggerUi3、UseSwaggerUi

  UseSwaggerUi3、UseSwaggerUi的作用和Swashbuckle的UseSwaggerUI作用是一樣,主要用於攔截swagger/index.html頁面請求,返回頁面給前端。

  UseSwaggerUi返回的是基於Swagger2.0的頁面,而UseSwaggerUi3返回的是基於Swagger3.0的頁面,所以這裡推薦使用UseSwaggerUi3  

    app.UseSwaggerUi3(settings =>
    {
        //中介軟體操作
    });

  SwaggerUi3Settings

  UseSwaggerUi3依賴SwaggerUi3Settings完成配置,SwaggerUi3Settings繼承於SwaggerUiSettingsBase和SwaggerSettings,所以屬性比較多,這裡介紹常用的一些屬性:

  EnableTryItOut

  這個屬性很簡單,就是設定允許你是否可以在SwaggerUI使用Try it out去呼叫介面

  DocumentTitle

  這是SwaggerUI頁面的Title資訊,也就是返回的html的head標籤下的title標籤值,預設是 Swagger UI

  CustomHeadContent

  自定義頁面head標籤內容,可以使用自定義的指令碼和樣式等等,作用於Swashbuckle中提到的HeadContent是一樣的

  Path

  Path是SwaggerUI的index.html頁面的地址,作用與Swashbuckle中提到的RoutePrefix是一樣的

  CustomInlineStyles

  自定外部樣式,不是連結,就是具體的樣式!

  CustomInlineStyles

  自定義的外部樣式檔案的連結

  CustomJavaScriptPath

  自定義外部JavaScript指令碼檔案的連線

  DocumentPath

  介面文件獲取架構swagger.json的Url模板,NSwag不需要想Swashbuckle呼叫SwaggerEndpoint新增文件就是因為它會自動根據這個將所有文件按照DocumentPath的格式進行設定,它的預設值是/swagger/{documentName}/swagger.json。

  同樣的,儘可能不要修改這個屬性,如果修改了,切記要和上面介紹的OpenApiDocumentMiddlewareSettings的Path屬性同步修改。

  SwaggerRoutes

  這是屬性包含了介面文件列表,在Swashbuckle中是通過SwaggerEndpoint方法新增的,但是NSwag會自動生成根據DocumentPath屬性自動生成。  

    app.UseSwaggerUi3(settings =>
    {
        settings.SwaggerRoutes.Add(new NSwag.AspNetCore.SwaggerUi3Route("demo", "/swagger/v1/swagger.json"));
    });

  需要注意的是,如果自己往SwaggerRoutes中新增介面文件物件,那麼NSwag不會自動生成了,比如上面的例子,雖然定義了多個文件,但是我們手動往SwaggerRoutes添加了一個,那SwaggerUI中就只會顯示我們自己手動新增的了。

  TransformToExternalPath

  TransformToExternalPath其實是一個路徑轉化,主要是轉換swagger內部的連線,比如獲取架構新的的請求 /swagger/v1/swagger.json和獲取swaggerUI頁面的連線 /swagger,這個很有用,比如上面提到的虛擬路徑處理的一個完整的例子: 

  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NSwag;

namespace NSwagDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOpenApiDocument(settings =>
            {
                settings.DocumentName = "v1";
                settings.Version = "v0.0.1";
                settings.Title = "測試介面專案";
                settings.Description = "介面文件說明";
                settings.ApiGroupNames = new string[] { "demo1" };

                settings.PostProcess = document =>
                {
                    document.Info.Contact = new OpenApiContact()
                    {
                        Name = "zhangsan",
                        Email = "[email protected]",
                        Url = null
                    };
                };

                settings.AddOperationFilter(context =>
                {
                    //我們的邏輯
                    return true;
                });

                //可以設定從註釋檔案載入,但是載入的內容可被OpenApiTagAttribute特性覆蓋
                settings.UseControllerSummaryAsTagDescription = true;

                //定義JwtBearer認證方式一
                settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
                {
                    Description = "這是方式一(直接在輸入框中輸入認證資訊,不需要在開頭新增Bearer)",
                    Name = "Authorization",//jwt預設的引數名稱
                    In = OpenApiSecurityApiKeyLocation.Header,//jwt預設存放Authorization資訊的位置(請求頭中)
                    Type = OpenApiSecuritySchemeType.Http,
                    Scheme = "bearer"
                });

                //定義JwtBearer認證方式二
                settings.AddSecurity("JwtBearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme()
                {
                    Description = "這是方式二(JWT授權(資料將在請求頭中進行傳輸) 直接在下框中輸入Bearer {token}(注意兩者之間是一個空格))",
                    Name = "Authorization",//jwt預設的引數名稱
                    In = OpenApiSecurityApiKeyLocation.Header,//jwt預設存放Authorization資訊的位置(請求頭中)
                    Type = OpenApiSecuritySchemeType.ApiKey
                });
            });

            services.AddAuthentication();
            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            //NSwag是虛擬路徑
            var documentPath = "/swagger/{documentName}/swagger.json";
            app.UseOpenApi(settings =>
            {
                settings.PostProcess = (document, request) =>
                {
                    //清理掉NSwag加上去的
                    document.Servers.Clear();
                    document.Servers.Add(new OpenApiServer() { Url = "http://localhost:90/NSwag", Description = "地址1" });
                    document.Servers.Add(new OpenApiServer() { Url = "http://127.0.0.1:90/NSwag", Description = "地址2" });
                    //192.168.28.213是我本地IP
                    document.Servers.Add(new OpenApiServer() { Url = "http://192.168.28.213:90/NSwag", Description = "地址3" });
                };
                settings.Path = documentPath;
            });
            app.UseSwaggerUi3(settings =>
            {
                //settings.SwaggerRoutes.Add(new NSwag.AspNetCore.SwaggerUi3Route("demo", "/swagger/v1/swagger.json"));
                settings.TransformToExternalPath = (s, r) =>
                 {

                     if (s.EndsWith("swagger.json"))
                     {
                         return $"/NSwag{s}";
                     }
                     return s;
                 };
            });

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
虛擬路徑例子

  比如這裡我們的虛擬路徑是NSwag,使用IIS部署:

  

  專案執行後

  

  

  四、總結

  後面還有東西就不寫了,還是那三個注意點:

  主要就是記住三點:

  1、服務注入使用AddOpenApiDocument方法(儘量不要用AddSwaggerDocument),主要就是生成介面相關資訊,如認證,介面註釋等等,還有幾種過濾器幫助我們實現自己的需求

  2、中介軟體注入有兩個:UseOpenApi(儘量不要使用UseSwagger,後續版本將會被移除)和UseSwaggerUi3(儘量不要使用UseSwaggerUi,後續版本將會被移除):

    UseOpenApi負責返回介面架構資訊,返回的是json格式的資料

    UseSwaggerUi3負責返回的是頁面資訊,返回的是html內容

  3、如果涉及到介面生成的,儘可能在AddOpenApiDocument中實現,如果涉及到UI頁面的,儘可能在UseSwaggerUi3中實現