- Основной: HttpClientHandler ()
Код: Выделить всё
AllowAutoRedirect = false - Делегирование: .AddHttpMessageHandler()
- Самый внешний: .AddResilienceHandler(...) с:
Код: Выделить всё
HttpRetryStrategyOptions { MaxRetryAttempts = 1, Delay = TimeSpan.Zero } - = 401
Код: Выделить всё
ShouldHandle - вызывает IAuthenticationTokenCache.ForceRefreshApiTokenAsync() (одноэлементный кеш)
Код: Выделить всё
OnRetry
Обработчик аутентификации отмечает заголовок каждой попытки:
Код: Выделить всё
protected override async Task SendAsync(HttpRequestMessage request, CancellationToken ct)
{
request.Headers.Remove("X-Auth-Handler-Stamp");
request.Headers.TryAddWithoutValidation("X-Auth-Handler-Stamp", Guid.NewGuid().ToString("N"));
var jwt = await _tokenCache.GetTokenForApi().ConfigureAwait(false);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
return await base.SendAsync(request, ct).ConfigureAwait(false);
}
Код: Выделить всё
services.AddHttpClient("my-http-client")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
AllowAutoRedirect = false
})
.AddHttpMessageHandler()
.AddResilienceHandler("RetryOnUnauthorized", (builder, sp) =>
{
var cache = sp.GetRequiredService(); // singleton
builder.AddRetry(new HttpRetryStrategyOptions
{
MaxRetryAttempts = 1,
Delay = TimeSpan.Zero,
ShouldHandle = new PredicateBuilder()
.HandleResult(r => r.StatusCode == HttpStatusCode.Unauthorized),
OnRetry = async _ => await cache.ForceRefreshApiTokenAsync().ConfigureAwait(false)
});
});
При первой ошибке 401 обработчик устойчивости повторяет попытку, весь конвейер запускается снова и изменяется X-Auth-Handler-Stamp.
Факт:
Я вижу два HTTP-вызова, но идентичные заголовки (включая X-Auth-Handler-Stamp) отправляются оба раза — как если бы то же самое HttpRequestMessage было отправлено повторно, а обработчик аутентификации больше не запускался.
Примечания:
- Используется клиент с правильным именем.
- Кэш одноэлементный и обновляется правильно.
- Нет перенаправлений, нет прокси-аутентификации, контент можно использовать повторно.
- Добавление .Handle() не изменило поведение.
Есть ли ситуация, когда повторная отправка происходит внутри HttpClientHandler (в обход обработчиков делегирования), или я неправильно понимаю поведение/порядок повторных попыток? Что мне следует сделать, чтобы гарантировать запуск обработчика аутентификации и получение нового токена?
Что я пробовал
- Построил конвейер с помощью IHttpClientFactory:
)Код: Выделить всё
HttpClientHandler (AllowAutoRedirect = false Код: Выделить всё
.AddHttpMessageHandler()- added last with HttpRetryStrategyOptions (retry on 401, MaxRetryAttempts = 1, Delay = TimeSpan.Zero).
Код: Выделить всё
.AddResilienceHandler(...)
- Singleton (используется как обработчиком, так и OnRetry).
Код: Выделить всё
IAuthenticationTokenCache - Делегирование обработчиков Transient.
[*]Поместил заголовок каждой попытки внутри обработчика аутентификации и перезаписал его при каждой отправке:
Код: Выделить всё
request.Headers.Remove("X-Auth-Handler-Stamp");
request.Headers.TryAddWithoutValidation("X-Auth-Handler-Stamp", Guid.NewGuid().ToString("N"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
Код: Выделить всё
private static readonly HttpRequestOptionsKey AttemptKey = new("Attempt");
request.Options.TryGetValue(AttemptKey, out var attempt);
request.Options.Set(AttemptKey, attempt + 1);
- 1-й вызов с старым токеном → возвращает 401 и развивает сценарий.
- 2-й вызов с новым токеном → должен вернуть 200.
[*]Содержимое подтвержденного запроса можно использовать повторно (нет потоков, недоступных для поиска).
Что я могу сделать? ожидается
- В случае 401 повторная попытка обеспечения устойчивости должна повторно войти во весь конвейер, поэтому:
выполняется дважды (один раз за попытку).Код: Выделить всё
AuthenticationDelegatingHandler.SendAsync - перезаписывается обновленным токеном при повторной попытке.
Код: Выделить всё
X-Auth-Handler-Stamp (GUID) changes between attempts. [*]AuthorizationЗаголовок - WireMock сопоставляет второй вызов с новым токеном и возвращает 200.
Что происходит на самом деле
- Я вижу два HTTP-вызова, но:
идентичен в обоих случаях.Код: Выделить всё
X-Auth-Handler-Stamp - Заголовки на уровне проводов идентичны; похоже, что то же самое сообщение HttpRequestMessage было отправлено повторно.
- Это говорит о том, что повторная отправка может происходить под обработчиками делегирования (внутри HttpClientHandler) или обработчик аутентификации не вызывается повторно при повторной попытке.
Любое минимальное воспроизведение/исправление приветствуется.
Подробнее здесь: https://stackoverflow.com/questions/797 ... message-re
Мобильная версия