0%

[DotnetCore]Refit:Polly應用

前情提要

筆者在公司負責的系統是跟第三方串接有關的系統,因此經常要串接第三方的APIWebServieDll等串接方式,若是API(Http Request)串接則,筆者這邊都全面改用Refit來串接,好處就不再贅述了,即便透過Refit來發Http Request,原本的DotnetCore預設的HttpClient相關的延伸應用,皆可延續使用,以此篇主題來說,筆者這邊想使用PollyRetry的機制,讓我們繼續看下去。

內容

筆者這邊對多個第三方串接的經驗來說,HttpRequest發出時的驗證,大概就遇過以下幾類

  • 定義好的SecretKey或其他設定值透過Header特定Key設定上去
  • 以第一種設定之外,有些第三方,還會有加密Request Body後塞在某一個HeaderKey上以驗證封包正確性
  • 有獨立Api可以取得AccessToken,並在每個ApiRequest上的Authorization Header上設定其取得的AccessToken

以獨立取得AccessToken來說,因Http連線成本比較高,若允許使用Redis的狀況下,就可以考慮將取得的AccessToken存於Redis上,並將過期時間設定為取得AccessToken時拿到的ExpireTimeRedis設定ExpireTime。所以會變成會先檢查Redis上面有無對應的AccessToken,有則直接取得並設定於AuthorizationHeader中,若無,則打一次取得AccessTokenAPI,將結果設定於Redis中,以便之後的Request取用。

但畢竟不是自己的系統,以筆者經驗來說,因時間差或第三方系統問題等等,在Redis上面的值尚未到期之前,還是有機會收到401,若沒有任何處理,可能都會以Redis上的AccessToken往第三方做驗證,止到Redis上面的ExpireTime到期為止,總不可能讓系統無法運作,還是想些辦法來解決這個問題。

筆者的思考脈絡就是,既然系統是靠Redis來決定要不要打取得AccessTokenAPI,就在Redis上動手腳,搭配Polly上的設定,收到401時的行為如下

  • Redis上的值刪除
  • 重試一次
    • 重發一次HttpRequest,這時Redis上的值被刪除,因此會再打一次取得AccessTokenAPI,將最新的AccessToken設定於Redis

以上兩步驟就能徹底解決問題

客製HttpMessageHandler

透過HttpMessageHandler,遇到401時將Redis上的值刪除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UnAuthorizeHttpMessageHandler : MyHttpMessageHandler
{
private readonly IRedisRepository _redisRepo;
public UnAuthorizeHttpMessageHandler(ApiRequestPipelineModel pipeline, ITimeWrapper timeWrapper, IApiLogRepository apiLogRepo, IRedisRepository redisRepo, IHttpContextAccessor httpContextAccessor) : base(pipeline, timeWrapper, apiLogRepo, httpContextAccessor)
{
_redisRepo = redisRepo;
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await _redisRepo.DeleteAsync("TOKEN");
}
return response;
}
}

上面出現的MyHttpMessageHandler為上幾篇有介紹到,需要的朋友請參考 [DotnetCore]Refit:HttpMessageHandler應用

Polly套用

這個部份已被微軟整合進http相關套件中,我們只要安裝Microsoft.Extensions.Http.Policy即可,宣告一下對應的retry機制

1
2
3
4
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(response => response.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(1);

註冊Refit

透過Refit宣告時將上面宣告好的Retry Policy掛到refit的設定中即可

  • 首先安裝Microsoft.Extensions.Http.Polly這個nuget套件
  • 接在AddRefitClient後面即可,ExtensionMethodAddPolicyHandler
1
2
3
4
5
6
7
services.AddRefitClient<ITestApiRepository>()
.ConfigureHttpClient(x =>
{
x.BaseAddress = new Uri(config["BaseUrl:Test"]);
})
.AddHttpMessageHandler<UnAuthorizeHttpMessageHandler>()
.AddPolicyHandler(retryPolicy);

結論

藉由這篇來介紹一下PollyHttpRequest時的應用,若需要判定HttpResponseStatusCode要做一些重試的動作,不妨可以試試搭配PollyRetryPolicy,設定上也方便,與主邏輯也分開,各司其職,肯定比自己手刻的Retry機制好,也比較不會有無法預測的SideEffect的出現,藉由這篇來了解一下,我們下篇見了。

參考