只有累積,沒有奇蹟

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年6月14日 星期五

    2019年6月13日 星期四

    [Tool] Log Parser Studio - 搜尋 Log 好幫手

    前言
    這幾天專案遇到些小亂流,必須找到特定條件的應用程式 Log 做資料的確認及重送機制,雖然系統有使用 ELK但由於專案太舊導致 Log 沒紀錄在 ELK 上面,為了快速找到多台應用程式中的 Log 資訊,在搜尋過程中爬 Log 使用 Log Parser 工具來快速搜尋 Log 資訊,避免過度花費人工的方式逐一進行搜尋 Log,這篇文章就針對 Log Paresr Studio 工具做簡單的介紹與說明,若有問題歡迎提出一起討論或是給予指導。

    安裝
    Log Parser 是一套強大的文字搜尋工具,可以針對單一或是多個 Log 檔案進行搜尋,支援多種 常見的Log 類型,最新版為 Log Parser 2.2 可以在微軟官網下載 : 傳送門 ,安裝後開啟應用程式如下
    可以輸入 command line 指定關鍵字與資料夾進行搜尋,舉例來說我想要在 C:\ Filemon 檔案搜尋 Access Denied 關鍵字,可以輸入下面指令取得
    LOGPARSER "Select Text from C:\Filemon.log where Text like '%Access Denied%'" -i:TEXTLINE -q:Off
    輸入後按下執行會跳出下列
    // 搜尋的結果
    
    Statistics
    Elements processed: 640444
    Elements output:    1
    Execution time:     12.75 seconds 
    如果對於使用指令搜尋有興趣,可以嘗試看看常用搜尋指令 : Log Parser Rocks! More than 50 Examples!

    Log Parser Studio
    我自己在使用上都是用 Log Parser Studio 工具,對於在使用上懶人會較為方便一些,Log Parser Studio 下載位置 : 傳送門,下載完畢開啟應用程式如下
    首頁會顯示常用的查詢 Lib 語法,可以提供使用者快速進行搜尋的動作,如果要自訂搜尋的話可以透過下列方式

    設定 Log
    在一開始使用前需要設定要搜尋的 Log 位置,設定步驟如下
    步驟如下

  • Step 1 : 新增 Query 
  • Step 2 : 開啟要查詢的 Log 檔案或是資料夾位置
  • Step 3 : 新增檔案
  • Step 4 : 設定完成

  • 查詢
    設定要搜尋的 Log 來源後 (單一檔案或是多個檔案) , 接著就是要使用最重要的搜尋功能,在 Log Parser Studio 工具下方可以輸入搜尋語法,主要搜尋欄位為 Text,以下例子為找 Log 中含有關鍵字 'Service -Start' 字眼的 Log 內容
    步驟如下

  • Step 1 : 選擇要查詢的 Log Type 
  • Step 2 : 在下方搜尋區塊輸入要找的關鍵字內容
  • Step 3 : 按下搜尋,查詢結果區塊就會顯示符合的內容

  • 匯出
    在 Log Parser Studio 中提供將查詢的結果匯出的功能,點擊左上角 File > export,選擇要輸出的檔案類型
    以上透過簡單的情境介紹了 Log Parser Studio 的基本使用方式,如果想要了解更多內容的話,可以透過作者的詳細介紹文章跟進階情境使用方式,來讓自己更熟悉這套強大的軟體

  • Getting started with Log Parser Studio
  • Getting started with Log Parser Studio, part 2
  • Getting started with Log Parser Studio, part 3
  • 希望可以透過以上簡單說明,讓你有知道有這麼好用的工具可以使用,也可以省下人工爬 Log 的困擾,如果下次有搜尋 Log 的需求時,不妨也可以考慮看看用 Log Parser Studio :)

    2019年6月11日 星期二

    [NETCore] Polly 的超時 TimeOut 和 Wrap 策略

    介紹
    Polly 是一套 .NET 處理瞬間故障的函式庫,提供開發人員用 Fluent API 方式及 Thread Safe 處理服務瞬間故障的策略,並提供重試(Retry)、斷路(Circuit-breaker)、超時(Timeout)、隔離(Bulkhead Isolation)、緩存(Cache)、回退(Fallback) 等機制,在上一篇 [NETCore] 使用 Polly 實現重試 (Retry) 策略 分享了 Retry 機制,這一篇就來介紹 Polly 中的 Timeout 策略,若有問題歡迎一起討論。

    Timeout 策略
    Polly 安裝與基本語法不再重複介紹,如果有興趣可以參考上一篇文章 : 傳送門。超時在開發中很常遇到,一般在發送 Request 到其他服務或是第三方伺服器時,都會設定 timeout 時間以避免使用者等待過久,過去在使用 httpClient 與 httpWebRequest 中可以透過 timeout 來調整等待設定,在 Polly 中設定 timeout 方式也是使用  Policy.Timeout  API 來設定逾時時間,簡單範例如下
    // 設定逾時時間為 10 秒
    Policy.Timeout(10);
    // async
    Policy.TimeoutAsync(10);
    Polly 在 Timeout API 也提供多載方法方便開發者設定,同樣也可以使用 Action 方法
    Policy.Timeout(10, (context, timespan, task) =>
    {
        // do something , ex : log.info
    });
    以下透過簡單範例代碼示範在 Polly 設定 Timeout 策略,在範例代碼中的 doTimeOutHTTPRequest 主要功能使用 httpclient 呼叫其他服務,為了 Demo 方便設定 timeout 時間為 3ms 讓請求出現 time exception,套用 Polly timeout 策略設定 timeout 為 1 ms,並在發生 timeout exception 時顯示 log 資訊
    static void Main(string[] args)
    {
        Policy
            .Timeout(TimeSpan.FromMilliseconds(1), onTimeout: (context, timespan, task) =>
            {        
                Console.WriteLine($"{context.PolicyKey} : execution timed out after {timespan} seconds.");
            })
            .Execute(doTimeOutHTTPRequest);
    }
    
    static string doTimeOutHTTPRequest()
    {
        Console.WriteLine($"開始發送 Request");
    
        HttpResponseMessage response;
        using (HttpClient client = new HttpClient())
        {
            client.Timeout = TimeSpan.FromMilliseconds(3);
            response = client.GetAsync(" http://www.mocky.io/v2/5cfed9a23200004f0045f284").Result;
        }
    
        return response.Content.ReadAsStringAsync().Result; 
    }
    執行後顯示內容如下
    如果開啟 debug mode 可以看到當 1ms 沒有完成任務,會觸發 TimeoutRejectedException 異常
    另外,在 Timeout 策略中可以設定 TimeoutStrategy,分為 Optimistic 與 Pessimistic 兩種設定值, 預設為 Optimistic 也就是支援發送取消 (CancellationToken),TimeoutStrategy.Optimistic 中設定 CancellationToken 代碼如下
    Policy timeoutPolicy = Policy.TimeoutAsync(30, TimeoutStrategy.Optimistic);
    HttpResponseMessage httpResponse = await timeoutPolicy
        .ExecuteAsync(
            async ct => await httpClient.GetAsync(requestEndpoint, ct), 
            CancellationToken.None 
        );
    
    如果對於 TimeoutStrategy 想解瞭更多的話,可以到官方 GitHub Timeout 文章中有針對 Optimistic Timeout 與 Pessimistic Timeout 使用上更詳細的說明與範例代碼 : 傳送門

    Wrap 策略
    Polly 支援多個故障時的策略機制,可以因應需求來組合其故障處理策略,這也是 Polly 彈性的設計,wrap 概念就像是策略包,可以將不同的故障策略包在一起設定為遇到故障時的處理機制方法,在組合時以下語法都支援
    // Execute
    fallback.Execute(() => waitAndRetry.Execute(() => breaker.Execute(action)));
    
    // Policy.Wrap
    Policy
      .Handle<SomeExceptionType>()
      .Retry(7)
    
    // equivalently sample 1
    fallback.Wrap(waitAndRetry.Wrap(breaker)).Execute(action);
    
    // equivalently sample 2
    fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);
    
    廢話不多說直接舉範例來說明,上一篇介紹了 Retry 策略這篇介紹了 Timeout 策略,我們就來結合兩個做為一個策略包,並在每個策略執行時將內容輸出在 console 上面
    static async Task Main(string[] args)
    {
        var timeoutPolicys = Policy
            .Timeout(TimeSpan.FromMilliseconds(1),
                onTimeout: (context, timespan, task) =>
                {
                    Console.WriteLine($"{context.PolicyKey} : execution timed out after {timespan} seconds.");
                });
    
        RetryPolicy waitAndRetryPolicy = Policy
            .Handle<Exception>()
            .Retry(3,
                onRetry: (exception, retryCount) =>
                {
                    Console.WriteLine($"[Polly retry] : 呼叫 API 異常, 進行第 {retryCount} 次重試");
                });
    
        FallbackPolicy<String> fallbackForTimeout = Policy<String>
            .Handle<TimeoutRejectedException>()
            .Fallback(
                fallbackValue: "Please try again later [Fallback for timeout]",
                onFallback: b => { Console.WriteLine($"這個請求超時了耶"); }
            );
    
        FallbackPolicy<String> fallbackForAnyException = Policy<String>
            .Handle<Exception>()
            .Fallback(
                fallbackAction: () => { return "Please try again later [Fallback for any exception]"; },
                onFallback: e => { Console.WriteLine($"[Polly fallback] : 重試失敗, say goodbye"); }
            );
    
        PolicyWrap<String> policyWrap = fallbackForAnyException.Wrap(fallbackForTimeout).Wrap(waitAndRetryPolicy)
            .Wrap(timeoutPolicys);
        policyWrap.Execute(() => doMockHTTPRequest());
    }
    
    static string doMockHTTPRequest()
    {
        Console.WriteLine($"開始發送 Request");
    
        HttpResponseMessage result;
        using (HttpClient client = new HttpClient())
        {
            client.Timeout = TimeSpan.FromMilliseconds(3);
            result = client.GetAsync("http://www.mocky.io/v2/5cfb4d9b3000006e080a8b0a").Result;
        }
    
        return result.Content.ReadAsStringAsync().Result;
    }
    
    執行之後輸出如下
    同樣的在官網中也有提供 wrap 的範例說明 : 傳送門。 

    感想
    在嘗試 wrap 的過程中花了不少時間跟踩雷,Handle 定義若是指定錯誤就會不會進到指定的故障策略中,舉例來說當 Timeout 時丟出的錯誤訊息為TimeoutRejectedException,若是沒有指定到此 exception 就不會進到 retry 機制當中,或是放大絕招直接handle exeption,在 Polly 的 wrap 中使用上還是有不少眉眉角角的細節要了解,也謝謝強者同事吉米斯提醒了重要的觀念才明白,日後若有比較值得分享的應用也會在PO出來,謝謝

    參考
    App-vNext/Polly


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

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com