我發現這個答案非常不完整:
使用 Polly 和 Typed Client 重繪 令牌
與這篇文章中的問題相同,除了接受的答案似乎存在嚴重缺陷。每次發出請求時,都會收到 401 錯誤,然后您獲取訪問令牌,將其附加到請求并重試。它可以作業,但是您在每條訊息上都會出錯。
我看到的唯一解決方案是設定默認身份驗證標頭,但要做到這一點,您需要HttpClient. 所以原來的答案需要做這樣的事情:
services.AddHttpClient<TypedClient>()
.AddPolicyHandler((provider, request) =>
{
return Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(1, (response, retryCount, context) =>
{
var httpClient = provider.GetRequiredService<HttpClient>();
var authService = provider.GetRequiredService<AuthService>();
httpClient.DefaultRequestHeaders.Authorization = authService.GetAccessToken());
});
});
});
所以問題是,您如何獲取當前HttpClient值以設定默認訪問令牌,這樣您就不會在每次發出請求時都呼叫此處理程式?我們只想在令牌過期時呼叫此處理程式。
uj5u.com熱心網友回復:
正如我在評論中所說,我已經創建了兩個解決方案:
- 一種利用自定義例外觸發重繪 和重試的方法
- 另一個使用 Polly 的背景關系來指示需要重繪
這兩種解決方案都使用命名客戶端。因此,在這里我將只關注需要更改以使用型別化客戶端的部分。
好訊息是您只需要更改解決方案的一小部分。
重寫基于自定義例外的解決方案
只需要更改以下代碼:
services.AddHttpClient("TestClient")
.AddPolicyHandler((provider, _) => GetTokenRefresher(provider))
.AddHttpMessageHandler<TokenFreshnessHandler>();
對此:
services.AddHttpClient<ITestClient, TestClient>
.AddPolicyHandler((provider, _) => GetTokenRefresher(provider))
.AddHttpMessageHandler<TokenFreshnessHandler>();
ITestClient和TestClient物體獨立于解決方案的其余部分。
重寫基于背景關系的解決方案
只需更改以下代碼
services.AddHttpClient("TestClient")
.AddPolicyHandler((sp, request) => GetTokenRefresher(sp, request))
.AddHttpMessageHandler<TokenRetrievalHandler>()
對此:
services.AddHttpClient<ITestClient, TestClient>
.AddPolicyHandler((sp, request) => GetTokenRefresher(sp, request))
.AddHttpMessageHandler<TokenRetrievalHandler>()
所以問題是,您如何獲取當前
HttpClient值以設定默認訪問令牌,這樣您就不會在每次發出請求時都呼叫此處理程式?我們只想在令牌過期時呼叫此處理程式。
使用我提出的解決方案,您無需訪問HttpClient來設定默認標頭,因為當前活動的訪問令牌存盤在單例類 ( TokenService) 中。在DelegatingHandler( TokenFreshnessHandler/ TokenRetrievalHandler) 中,您檢索最新、最大的令牌并將其設定在 HttpRequestMessage.
uj5u.com熱心網友回復:
Peter Csala 的回答是一個很好的開始。但是,我發現“背景關系”的生命周期僅與 HttpRequestMessage 一樣長,基本上每個 HttpClient 函式呼叫(Put、Post、Get、Delete、Send)一次。也就是說,它不會從函式呼叫到函式呼叫持續存在,因此我們不斷地向快取請求令牌。在 MSAL 中,此功能并非完全無關緊要,并且涉及背景關系切換。我們應該能夠只使用存盤的字串,直到出現 401 錯誤,然后向 MSAL 快取請求新令牌是合理的。
我相信這是 TokenRetrievalService 處理程式的更高效版本:
public class TokenRetrievalHandler : DelegatingHandler
{
private readonly ITokenService tokenService;
public TokenRetrievalHandler(ITokenService tokenService)
{
this.tokenService = tokenService;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.tokenService.AccessToken);
return await base.SendAsync(request, cancellationToken);
}
}
這是令牌服務的更高效版本:
public class TokenService : ITokenService
{
public string AccessToken { get; set; }
public async Task RefreshAccessTokenAsync()
{
this.AccessToken = await <GetTokenFromPromptorCache>();
}
}
然后令牌重繪 策略看起來要簡單得多:
private static IAsyncPolicy<HttpResponseMessage> GetTokenRefresher(IServiceProvider serviceProvider, HttpRequestMessage httpRequestMessage)
{
return Policy<HttpResponseMessage>
.HandleResult(response => response.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(async (handler, retry) =>
{
await serviceProvider.GetRequiredService<ITokenService>().RefreshAccessTokenAsync();
});
}
再次感謝 Peter Csala 的大部分作業。這是一個小的調整,它實作了重用令牌原始版本的原始目標,而不會在每次呼叫時(例如)打擾 MSAL 快取。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/511747.html
