<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          新版本 Swashbuckle swagger 組件中的 "坑"

          共 3918字,需瀏覽 8分鐘

           ·

          2020-11-22 12:17

          新版本 Swashbuckle swagger 組件中的 Servers 坑

          Intro

          上周做了公司的項(xiàng)目升級(jí),從 2.2 更新到 3.1, swagger 直接更新到了最新,swagger 用的組件是 Swashbuckle.AspNetCore,然后遇到一個(gè) swagger 的問題, 在本地測(cè)試是沒問題的,但是部署在測(cè)試環(huán)境之后就會(huì)有問題,主要是 swagger 界面會(huì)多一個(gè) servers 的選項(xiàng),可能會(huì)導(dǎo)致 swagger 不能正常使用,下面詳細(xì)介紹一下

          Swagger "bug" reproduce

          大概的問題是這樣的,在本地環(huán)境是好的,在測(cè)試環(huán)境部署是有問題,測(cè)試環(huán)境部署之后的 swagger 界面大致如下:

          很明顯這個(gè) servers 是有問題的,我們實(shí)際訪問的地址是 https://testserver/swagger 這樣的地址,但是 swagger 內(nèi)部拼出來的 server 地址和實(shí)際訪問的地址是不符的,swagger 生成的 open api 文檔里也會(huì)有一個(gè) servers 的屬性,示例如下:

          這會(huì)導(dǎo)致我們使用 swagger 調(diào)試 API 的時(shí)候會(huì)走一個(gè)錯(cuò)誤的 server 地址,實(shí)際請(qǐng)求的地址是 sever 地址加上 api path,可以看一個(gè)示例

          Dig the Source

          Swashbuckle.AspNetCore 是開源的,我們就是扒一扒它的實(shí)現(xiàn)源碼吧,我們用的是 5.6.3 版本,直接看 5.6.3 tag 對(duì)應(yīng)的代碼,可以找到 swagger 的中間件

          https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs

          在這里我們可以看到,再返回給客戶端之前 open api 文檔響應(yīng)之前我們是可以看到,是會(huì)經(jīng)過 PreSerializeFilters 處理的,我們?cè)僭敿?xì)看一下 swaggerProvider.GetSwagger 的實(shí)現(xiàn)

          實(shí)現(xiàn)代碼在這里(可以通過服務(wù)注冊(cè)找到對(duì)應(yīng)的實(shí)現(xiàn),也可以直接找對(duì)應(yīng)接口的實(shí)現(xiàn))

          https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L31

          二者結(jié)合來看,servers 會(huì)根據(jù)用戶請(qǐng)求來獲取一個(gè) server 地址,而當(dāng)有 X-Forwarded-Host 請(qǐng)求頭的時(shí)候如果沒有按照 swagger 指定的規(guī)則這樣進(jìn)行請(qǐng)求頭的轉(zhuǎn)發(fā)就會(huì)導(dǎo)致有問題,而我們的測(cè)試環(huán)境也正是因?yàn)槿绱?,測(cè)試環(huán)境有一層 LB,經(jīng)過 LB 轉(zhuǎn)發(fā)了 X-Forwarded-HostX-Forwarded-Proto 請(qǐng)求頭,但是沒有轉(zhuǎn)發(fā) X-Forwarded-Port 所以經(jīng)過 swagger 的處理之后,就從 https://testserver 變成了 https://testserver:80 這樣

          private?string?GetHostOrNullFromRequest(HttpRequest?request)
          {
          ????if?(!request.Headers.TryGetValue("X-Forwarded-Host",?out?StringValues?forwardedHost))
          ????????return?null;

          ????var?hostBuilder?=?new?UriBuilder($"http://{forwardedHost[0]}");

          ????if?(request.Headers.TryGetValue("X-Forwarded-Proto",?out?StringValues?forwardedProto))
          ????????hostBuilder.Scheme?=?forwardedProto[0];

          ????if?(request.Headers.TryGetValue("X-Forwarded-Port",?out?StringValues?forwardedPort))
          ????????hostBuilder.Port?=?int.Parse(forwardedPort[0]);

          ????return?hostBuilder.Uri.ToString().Trim('/');
          }

          private?string?GetBasePathOrNullFromRequest(HttpRequest?request)
          {
          ????var?pathBuilder?=?new?StringBuilder();

          ????if?(request.Headers.TryGetValue("X-Forwarded-Prefix",?out?StringValues?forwardedPrefix))
          ????????pathBuilder.Append(forwardedPrefix[0].TrimEnd('/'));

          ????if?(request.PathBase.HasValue)
          ????????pathBuilder.Append(request.PathBase.Value.TrimEnd('/'));

          ????return?(pathBuilder.Length?>?0)
          ??????????pathBuilder.ToString()
          ????????:?null;
          }

          解決方案

          從上面的源碼中基本就可以分析出問題的原因來,解決的辦法我覺得有下面幾種:

          1. LB 轉(zhuǎn)發(fā)的時(shí)候帶上 X-Forwarded-Port 請(qǐng)求頭,轉(zhuǎn)發(fā)原始請(qǐng)求的端口號(hào)(需要 LB 轉(zhuǎn)發(fā)自己能夠控制,我們?nèi)绻渲眠€需要讓 DevOps 的童鞋幫忙弄,如果完全是自己控制的就比較方便【推薦】)
          2. 在使用 Swagger 中間件之前把 X-Forwarded-Port 請(qǐng)求頭設(shè)置為 443(不夠靈活,如果訪問 LB 是 http 或者有特別的端口號(hào)就會(huì)有問題)
          3. 在使用 swagger 中間件之前把 X-Forwarded-Host 請(qǐng)求頭移除掉,這樣就不會(huì)有 servers 這個(gè)屬性了(感覺不夠優(yōu)雅)
          4. 注冊(cè)一個(gè) PreSerializeFilter 把 Servers 清空,實(shí)現(xiàn)代碼如下(【推薦】,沒有 servers 屬性的時(shí)候完全按請(qǐng)求 swagger 的 baseUrl 來作為 api 的前綴,示例代碼如下)
          app.UseSwagger(c?=>
          {
          ????c.PreSerializeFilters.Add((doc,?_)?=>
          ????{
          ????????doc.Servers?.Clear();
          ????});
          });

          更新之后就沒有 servers 屬性了,和之前的版本保持一致了

          More

          我們使用的是 5.6.3 版本,應(yīng)該從 5.6.0 開始都有這個(gè)問題,如果遇到了這個(gè)問題不要慌哈,參考上面的解決方案即可

          我覺得 swagger 這樣的實(shí)現(xiàn)方式不太友好,更好的實(shí)現(xiàn)應(yīng)該結(jié)合微軟的 ForwardHeaders 中間件來實(shí)現(xiàn),Swagger 組件作者表示已經(jīng)有計(jì)劃,打算在 6.0 的時(shí)候更新結(jié)合微軟的中間件來實(shí)現(xiàn),詳細(xì)可以參考 Github 上的 Issue https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1814

          Reference

          • https://github.com/domaindrivendev/Swashbuckle.AspNetCore
          • https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs
          • https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L31
          • https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1814







          回復(fù)?【關(guān)閉】學(xué)關(guān)
          回復(fù)?【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù)?【被刪】學(xué)個(gè)
          回復(fù)?【訪客】學(xué)
          回復(fù)?【小程序】學(xué)獲取15套【入門+實(shí)戰(zhàn)+賺錢】小程序源碼
          回復(fù)?【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù)?【2019】獲取2019 .NET 開發(fā)者峰會(huì)資料PPT
          回復(fù)?【加群】加入dotnet微信交流群

          副業(yè)剛需,個(gè)人開發(fā)者如何通過小程序變現(xiàn)?已經(jīng)有朋友變現(xiàn)月入4k了!


          微信錢包“免費(fèi)提現(xiàn)”的方法來了!


          瀏覽 71
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  香蕉久久国产AV一区二区 | 一区二区三区四区精品视频 | 水多多成人网站A片在线观看 | 欧美性爱乱伦 | 免费黄色在线 |