我正在使用Service Bus主题触发器Azure函数,在向外部API发出HTTP请求时,我需要处理401(未经授权)错误,例如刷新承载令牌和重试失败的请求.
我正在考虑两种方法,我不确定哪一种更合适. 对于哪种方法是最好的,我将不胜感激.
这是我的重试策略类,包含针对401错误的未经授权的重试策略.
public class PollyRetryPolicy : IPollyRetryPolicy
{
private readonly ILogger<PollyRetryPolicy> _logger;
private readonly IOptions<RetryPolicyConfigOptions> _options;
public PollyRetryPolicy(ILogger<PollyRetryPolicy> logger,
IOptions<RetryPolicyConfigOptions> options)
{
_logger = logger;
_options = options;
_jitterer = new Random();
}
public IAsyncPolicy GetUnauthorizedRetryPolicy()
{
var policy = Policy
.Handle<HttpRequestException>(ex => ex.StatusCode == HttpStatusCode.Unauthorized)
.WaitAndRetryAsync(
_options.Value.MaxRetryAttempts,
retryAttempt => TimeSpan.Zero); // Set the delay to zero milliseconds for immediate retry
return policy;
}
}
这是我的API客户端,我试图在其中调用第一个承载令牌端点,并使用IMory yCache存储承载令牌,然后使用该承载令牌进行实际调用.
public class NotificationApiClient : INotificationApiClient
{
private readonly ILogger<NotificationApiClient> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IMemoryCache _memoryCache;
private readonly NotificationApiConfigOption _notificationApiConfigOption;
public NotificationApiClient(ILogger<NotificationApiClient> logger,
IHttpClientFactory httpClientFactory,
IMemoryCache memoryCache,
IOptions<NotificationApiConfigOption> notificationApiConfigOption)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_memoryCache = memoryCache;
_notificationApiConfigOption = notificationApiConfigOption.Value;
}
public async Task<bool> CheckDeliveryAccessAsync(int code)
{
try
{
var httpClient = _httpClientFactory.CreateClient();
httpClient.BaseAddress = new Uri(_notificationApiConfigOption.BaseUri);
// Get the bearer token
string bearerToken = await GetNotificationAccessTokenAsync();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
var apiResponse = await httpClient.GetAsync($"{_notificationApiConfigOption.CheckDeliveryAccessApiUri}?code={code}");
if (apiResponse.StatusCode == HttpStatusCode.OK)
{
string resultContent = await apiResponse.Content.ReadAsStringAsync();
var result = resultContent.AsPoco<CheckInvoiceDeliveryAccessResponse>();
return result.Data.IsActive;
}
else
{
return false;
}
}
catch (Exception ex)
{
throw;
}
}
public async Task<string> GetNotificationAccessTokenAsync()
{
try
{
if (_memoryCache.TryGetValue("BearerToken", out string cachedBearerToken))
{
return cachedBearerToken;
}
var httpClient = _httpClientFactory.CreateClient();
httpClient.BaseAddress = new Uri(_notificationApiConfigOption.BaseUri);
var formData = new Dictionary<string, string>
{
{ "clientId", _notificationApiConfigOption.ClientId },
{ "clientSecret", _notificationApiConfigOption.ClientSecret }
};
var content = new FormUrlEncodedContent(formData);
var apiResponse = await httpClient.PostAsync($"/{_notificationApiConfigOption.AccessTokenUri}", content);
if (apiResponse.StatusCode == HttpStatusCode.OK)
{
string resultContent = await apiResponse.Content.ReadAsStringAsync();
var result = resultContent.AsPoco<NotificationAccessTokenResponse>();
_memoryCache.Set("BearerToken", result.AccessToken, new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(result.ExpiresIn)
});
return result.AccessToken;
}
else
{
return string.Empty;
}
}
catch (Exception ex)
{
throw;
}
}
}