只有累積,沒有奇蹟

顯示具有 Swagger 標籤的文章。 顯示所有文章
顯示具有 Swagger 標籤的文章。 顯示所有文章

2022年4月28日 星期四

[WEB API] Swagger - 在 Headers 中新增 API Token 驗證

問題
在開發 API 時都會在網站加上 API Token 機制,當收到一個 Request 請求時 API 會驗證 Token 的正確性,確認請求參數中的 Token 是否是有效 / 已授權 / 有沒有過期或是用來當 SSO (Single sign-on) 的使用,驗證無誤後才會進入接口的邏輯處理目前公司內部 API 專案也有驗證 Token 的設計之前文章介紹了 Swagger 基本使用 發現在線上文件進行 API 測試沒有 Token 的話根本無法測試,此篇記錄如果遇到此問題時該如何解決

解決方案
Swagger 在產生 API 接口會將參數 (Model) 資訊顯示在 html 文件,但如果驗證 Token 機制是在 Header時所生成的 Web API 文件就不會生成在網頁上,寫了一個簡單的範例專案讓大家比較好了解 (僅示意,相信寫法上有更多好的驗證機制)如下圖Code 內容所示,程式碼在進入時就檢查 Request Header 是否有提供 Token 參數,此時使用 Swagger 就是沒有地方可以輸入 Token 參數,因此測試就會因為不符合 Token 驗證會一直爆炸
 
該如何解決呢? 在 Swashbuckle 是很容易擴充的,可以透過加入 IOperationFilter 達到在線上文件新增驗證 Header 的需求,更符合測試上的情境,以下為研究後的小小心得

新增類別並實作 IOperationFilter 
以範例程式為例新增 HeaderTokenOperationFilter 類別並實作 IOperationFilter 介面中的方法 Apply
public class HeaderTokenOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
 if (operation.parameters == null)
  operation.parameters = new List();

 operation.parameters.Add(new Parameter
 {
  name = "Token",
  @in = "header", //query
  type = "string",
  description = "User Token In Header",
  required = true
 });
    }
}

程式說明 : 
  • Line 10 : 要加入的 Parameter 名稱
  • Line 11 : 請求時所要放的位置,此範例是在 Header,也可以放在 QueryString
  • Line 12 : 參數型態
  • Line 13 : Parameter 說明
  • Line 14 : 是否必填
設定 SwaggerConfig
開啟 SwaggerConfig.cs 檔案加入下列程式
驗證
重新開啟專案,發現 Swagger 已經有 Token 參數可以提供輸入並會顯示為必填欄位
為了驗證是否真的有在 Request 的 Headers 帶 Token 參數,可以透過 Chrome F12 來觀察是否有帶正確的 Token 參數 : F12 > Network > Headers 
驗證無誤,打完收工

完整 Sample
完整的 Code 有需要請自行服用
    public class HomeController : ApiController
    {
        public string _userSsoToken = "1aa2a116-876e-464b-bdaf-d6d3adaeb4e4";
        
        /// 
        /// 取得使用者金額
        /// 
        /// 
        [HttpPost]
        [Route("GetMoney")]
        [SwaggerRequestExample(typeof(MemberUser), typeof(MemberUserExample))]
        public IHttpActionResult GetMoney(MemberUser input)
        {
            var userInfo = new UserInfo();
            
            // 檢查 Header 是否有 token
            if (Request.Headers.Contains("Token"))
            {
                string token = Request.Headers.GetValues("Token").First();

                if (token == _userSsoToken)
                {
                    // 驗證無誤 回傳資料
                    if (input.UserId == 9487)
                    {
                        GetUserInfo(userInfo);
                    }
                }
                else
                {
                    // 驗證錯誤
                    TokenError(userInfo);
                }

                userInfo.Token = token;
            }
            
            return Ok(userInfo);
        }

        private static void TokenError(UserInfo userInfo)
        {
            userInfo.Code = "999";
            userInfo.Message = "Token error";
        }

        private static void GetUserInfo(UserInfo userInfo)
        {
            userInfo.Code = "000";
            userInfo.Message = "Success";
            userInfo.UserId = "9487";
            userInfo.Name = "marcus";
            userInfo.Money = "1000";
        }
    }
    
    internal class UserInfo
    {
        public string UserId { get; set; }
        public string Name { get; set; }
        public string Money { get; set; }
        public string Code { get; set; }
        public string Message { get; set; }
        public string Token { get; set; }
    }

    public class MemberUser
    {
        public int UserId { get; set; }
    }

    public class MemberUserExample : IExamplesProvider
    {
        public object GetExamples()
        {
            return new MemberUser
            {
                UserId = 9487,
            };
        }
    }
    public class HeaderTokenOperationFilter : IOperationFilter
    {
        public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
        {
            if (operation.parameters == null)
                operation.parameters = new List();

            operation.parameters.Add(new Parameter
            {
                name = "Token",
                @in = "header",
                type = "string",
                description = "User Token In Header",
                required = true
            });
        }
    }
    參考
    add-an-authorization-header-to-your-swagger-ui-with-swashbuckle
    .NET -Swagger Web API for Basic Authentication

    2022年4月10日 星期日

    [WEBAPI] Swagger - 用 Swashbuckle.Examples 加上有意義的測試數據

    問題
    Swagger 是一個可以將 WebAPI 快速文件化的套件,產生出來的線上文件除了可以列出 API 詳細資料外還可以直接在網頁上進行測試的動作,對開發者和接 API 的使用者來說十分方便上一篇文章介紹了 Swagger 基本使用 說明最近想要在公司內部推廣使用 Swagger 服務,資深同事提到過去有陣子曾經使用過 Swagger 服務但  每次要使用 API 接口服務時參數 (params) 資訊都要重新輸入 ,有點麻煩用一陣子之後大家就回去使用 Postman 了,了解後發現的確在測試時會花時間在輸入測試資料,如果測試環境測試資料都是固定的,就可省下輸入資料的時間更可避免 key 錯資料的狀況發生 ( 參考下方 gif 檔案)這邊文章記錄解決此問題的過程
    解決方案
    Swagger 在產生 API 接口的 Model 時取得該物件的 properties 及其對應型別,將其物件資訊顯示在 html文件但不會生成 "可以測試的真實數據 (或是你想要的)" 資料,也就是說替換下圖的紅色框框資料,讓它是有意義的資料搜尋後發現可以使用 Swashbuckle.Examples 來滿足我們小小的需求以下整理研究後的步驟與使用說明

    安裝 Swashbuckle.Examples
    Step 1. 開啟 nuget > 輸入 Swashbuckle.Examples 並下載安裝
    Step 2. 目前最新版是 3.10.0 且不會額外下載其他特別的 dll > 下一步
    Step 3. 可以到專案參考是否有 Swashbuckle.Examples dll,有的話就是下載完成

    實作 Request Sample
    接下來在 Method 上加上 SwaggerRequestExample Attribute第一個參數是原本的 Model第二個參數是範例 Model 名稱以範例專案的例子來說Login 接口第一個參數為 User (原本的 Model) 第二個參數 UserExample (新 Model)
    [SwaggerRequestExample(typeof(Users), typeof(UserExample))]
    public IHttpActionResult Login(Users input) 
    接者新增 UserExample 類別並實作 IExamplesProvider 介面,並將我們指定的測試資料定義在該介面的 GetExample 方法中
    public class UserExample : IExamplesProvider
    {
        public object GetExamples()
        {
     return new Users
     {
      Account = "marcus",
      Password = "123456",
      Key = 9487,
     };
        }
    }
    
    完整的 Code 請參考
    public class HomeController : ApiController
    {   
        [Route("Login")]
        [SwaggerRequestExample(typeof(Users), typeof(UserExample))]
        public IHttpActionResult Login(Users input)
        {
         var rep = new ResponseObject();
    
     if (input.Account == "marcus" 
      && input.Password == "123456" 
      && input.Key == 9487)
     {
      rep.Code = "000";
      rep.Message = "Success";
      rep.Token = Guid.NewGuid();
     }
     else
     {
      rep.Code = "999";
      rep.Message = "Please check your input params";
     }
    
     return Ok(rep);
        }
    }
    
    public class Users
    {
        public string Password { get; set; }
        public string Account { get; set; }
        public int Key { get; set; }
    }
    
    public class UserExample : IExamplesProvider
    {
        public object GetExamples()
        {
     return new Users
     {
      Account = "marcus",
      Password = "123456",
      Key = 9487,
     };
        }
    }
    
    public class ResponseObject
    {
        public string Code { get; set; }
        public string Message { get; set; }
        public Guid Token { get; set; }
    }
    
    設定 SwaggerConfig
    安裝完 Swagger.example package,在 Code 指定好需要產生的 example 物件,下一步是要在 Swagger 設定 OperationFilter開啟 SwaggerConfig.cs 檔案加入下列程式

    打完收工 
    透過以上設定,在重新開啟 Swagger 就可以發現 example model 已生效,省下了輸入 API 參數的時間可以更專注的在測試 / 驗證接口正確性,提升效率早點下班回家為了證明哥沒有在唬爛調整後的畫面如下
    Swashbuckle.Examples 在使用上簡單容易上手,但除了可以透過此套件定義 Example Request Model 之外,官網文件上與有提到可以設定 Example Response Model、Description、Authorization...等更多資訊,也支持 .NET Core 版本,如果有需要各位大大也可以自行研究看看
    Summary
    這問題聽到當下嚇到吃手手,江湖上常聽到攻城屍很懶惰但沒想到懶到這程度,但仔細了解後發現問題確實存在,且驗證後發現,在此範例三個參數使用完整整省去一半的時間 18 sec > 9sec想到之前參加研討會講師所講的,懶是一個美德否則會局限自己的成長

    參考
    Swashbuckle.Examples
    Swashbuckle Pro Tips for ASP.NET Web API – Example(s) Using AutoFixture
    Swagger for Web API Document – Part Ⅱ

    2019年6月20日 星期四

    [WEBAPI] 如何設定 Swagger 為預設首頁 Start Page

    問題
    自己不管是在開發 .NET Framework 或是 .NET Core 專案,只要遇到是 Web API 專案都會安裝 Swagger 來協助測試 API 的動作,只要使用過 Swagger 的開發者都知道啟動專案後第一件事就是要在開啟的頁面網址加上 Swagger,接著在繼續透過 Swagger 頁面進行 API 測試的動作,但每次開啟時就需要在網址上輸入一次也是頗麻煩的,今天就來分享兩種方式可以起始頁面為 swagger 頁面若有問題歡迎提出一起討論或是給予指導。

    解決方案
    在專案設定中可以調整專案起始的頁面,設定方式如下
    專案點擊右鍵 > 選擇 Web > Start Action 部分選擇 Specific Page > 輸入 swagger 
    設定完畢後按下存檔,在重新開啟
    解決方案- webAPIConfig
    如果希望在 Code 裡面做調整的話,則可以在 App_Start 底下的 WebAPIConfig.cs 加入下段程式碼,主要是透過   Swashbuckle.Core  中的 RedirectHandler 類別定義 redirectPath
    config.Routes.MapHttpRoute(
        name: "swagger_root",
        routeTemplate: "",
        defaults: null,
        constraints: null,
        handler: new RedirectHandler((message => message.RequestUri.ToString()), "swagger"));
    透過反射來看一下 RedirectHandler 類別內容,主要是繼承 HttpMessageHandler,並透過 rootUrlResolver 與 redirectPath 做到轉頁的功能。
    namespace Swashbuckle.Application
    {
      public class RedirectHandler : HttpMessageHandler
      {
        private readonly Func<HttpRequestMessage, string> _rootUrlResolver;
        private readonly string _redirectPath;
    
        public RedirectHandler(Func<HttpRequestMessage, string> rootUrlResolver, string redirectPath)
        {
          this._rootUrlResolver = rootUrlResolver;
          this._redirectPath = redirectPath;
        }
    
        protected override Task<HttpResponseMessage> SendAsync(
          HttpRequestMessage request,
          CancellationToken cancellationToken)
        {
          string uriString = this._rootUrlResolver(request) + "/" + this._redirectPath;
          HttpResponseMessage response = HttpRequestMessageExtensions.CreateResponse(request, HttpStatusCode.MovedPermanently);
          response.Headers.Location = new Uri(uriString);
          TaskCompletionSource<HttpResponseMessage> completionSource = new TaskCompletionSource<HttpResponseMessage>();
          completionSource.SetResult(response);
          return completionSource.Task;
        }
      }
    }
    
    加入代碼進行編譯後,在重新啟動結果與上述相同首頁都為 swagger 頁面,這裡就不在重複貼首頁


    感想
    自己過去都是使用第一個方式來設定首頁,今天在與強者同事討論中分享了不同的方法給我,讓我學到了不少,如果有需要的各位也可以自行挑選哪種最適合你,以上如果有問題歡迎提出討論,Happy Coding !
      參考
      How to set Swagger as default start page?

      2019年1月3日 星期四

      [WEB API] 使用 Swagger 自動產生 WebAPI 技術文件

      Swagger 是什麼
      以下是 Swagger 官網說明
      “ Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API “ 
      Swagger 是一個可以將你的 API 接口變成可視化的服務透過 Swagger 提供的工具能動態產生HTML、CSS、Javascript 網頁版的文件相對應的接口清單,接口的使用方式 (HTTP Method)、API 簽章(參數名稱) 以及直接在網頁上進行接口測試的動作,驗證 API 接口服務是否正常

      為什麼要用 Swagger
      好的開發文件可以讓 User 清楚知道如何使用 API 接口,但大部分的工程師對於寫文件這件事都沒什麼好感,但偏偏在開發時又希望有清楚的文件 (好難搞...),以下整理個人覺得帶來的好處
      • 建立文件 : 專案有寫出來的 Code 等同於技術文件減少額外寫文件及維護文件的成本
      • 方便溝通 : 讓你的服務端使用者像是前端、QA、Support 同仁清楚的知道接口相關資訊
      • 方便測試 : 使用線上文件進行測試,透過網頁立即知道 API 返回的結果,節省溝通時間提升效率
      如何使用
      要在 WebAPI 專案使用 Swagger 很簡單,只需要透過 Nuget 下載安裝 Swagger 相關 package即可以下整理基本安裝過程與步驟

      安裝 Swashbuckle
      Step 1. 在 Web API 專案按右鍵 > Manage Nuget Package
      Step 2. 點選 Browser > 搜尋框輸入 Swashbuckle > 下載
      Step 3. 安裝 Swashbuckle 過程中會一併安裝 Swashbuckle.Core 套件
      Step 4. 安裝完畢會提示 Web.Config 需要 Reload選擇  Yes to all
      Step 5. 如何確定有安裝成功 ? 可以到專案點開 reference > 確認有 Swashbuckle.Core 就代表安裝完成啦
      設定專案 XML 註解
      安裝完畢後接下來設定專案輸出的 XML 註解,註解來源是從 Class 上方按下三個反斜線 "///" 區塊內容擷取的,專案預設不會開啟需要手動設定輸出註解位置Swagger 在自動產生技術文件需要此資訊
      Step 1. 在 Web API 專案按右鍵 > 選擇 Properties
      Step 2. Tab 選擇 Build > 選取 XML documentation file 路徑輸入 XML 要產生的路徑位置
      Step 3. 接著在 Controller 方法加上註解按下 "///" 會自動產生 Comment
      設定完後每次專案建置完會在 App_Data folder 產生 XML 檔案,但 Visual Studio 在 compiler 同時也會檢查專案中哪些地方尚未加上 XML Comment (Compiler warning CS1591),會在建置完後提示如下圖片顯示
      Step 4. 覺得不想看到沒關係把它關掉就好 : 選擇 Build > Errors and Warnings > Suppress warnings 輸入 1591透過設定不再提示 missing xml Warnings 訊息資訊

      設定 SwaggerConfig
      安裝完 Swagger package,產生專案 XML Comment,下一步就是告訴 Swagger 產生線上文件的位置Swagger 安裝完後會在專案 App_Start 資料夾下新增 SwaggerConfig.cs 檔案,開啟檔案做以下調整
      Step 1. 在 SwaggerConfig.cs 搜尋 IncludeXmlComments > 將此行反註解
      Step 2. 註解拿掉後會發現缺少 GetXmlCommentsPath 方法,這邊就是定義剛專案檔設定的 XML Document 位置,因此直接加上此方法與 Code 如下
      private static  string GetXmlCommentsPath()
      {
          return String.Format(
              @"{0}\App_Data\XmlDocument.xml",
              AppDomain.CurrentDomain.BaseDirectory);
      }
      
      使用 swagger 
      完成上述步驟你衷心盼望的 WEB API 文件就完成了在 Application 網址後加上 /swagger,就可以看到swagger 幫你產生的 API 線上文件
      可以看到範例專案中 ValueController 底下有提供五個 API 接口,呼叫 API 時分別要透過 HTTP 的哪種 Method 方法以及 RouteAction Name 名稱點擊 Method 會提供更多 API 細節
      簡單說明 : 
      • 1. API 註解 : 稍早在 Code 加上的註解 (XML) 已呈現在網頁,除此之外還列出此接口所需要的參數型別資訊
      • 2. 測試接口 : 按下 Try it out,可以直接根據你所輸入的參數直接打 API 位置,方便驗證正確性
      • 3. 測試回傳 : 可以看到打完 API 後回傳資訊,像是 Header 內容、Status Code、Response 物件
      Summary
      Swagger 幫助我們線上Web API Application 產生文件,透過一些簡單的設定加上 XML Remark就能夠達到這件事,讓寫文件這件事變得沒那麼無聊,這篇分享 Swagger 基本的操作說明及用法,之後預計會(咦 是拖稿嗎)在分享更多實務上遇到的情境需求及使用心得

      參考
      Swagger初探
      swagger-ui
      ASP.NET Web API 文件產生器 - 使用 Swagger

      Copyright © m@rcus 學習筆記 | Powered by Blogger

      Design by Anders Noren | Blogger Theme by NewBloggerThemes.com