Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Gdy interfejs API jest zbyt wolny, w aplikacji .NET występuje 1 z 2 wyjątków, w zależności od tego, który limit czasu został wyzwolony.
HttpClient.Timeout zgłasza wyjątek TaskCanceledException. Standardowa procedura obsługi odporności z Microsoft.Extensions.Http.Resilience zgłasza wyjątek TimeoutRejectedException z Polly. Pochodzą one z różnych miejsc, mają różne wartości domyślne i wymagają oddzielnych catch bloków.
Który timeout wystąpił
| Przerwa czasowa | Wartość domyślna | Co otrzymuje Twój kod | Gdzie ją ustawiasz |
|---|---|---|---|
HttpClient.Timeout |
100 sekund |
TaskCanceledException, z TimeoutException jako InnerException (.NET 5 i nowsze) |
HttpClient.Timeout |
| Limit czasu próby standardowego programu obsługi | 10 sekund na próbę | Na początku nic: obsługa ponawia próbę | AddStandardResilienceHandler(options => ...) |
| Całkowity limit czasu standardowej procedury obsługi | 30 sekund, w tym wszystkie ponowienia prób | Polly.Timeout.TimeoutRejectedException |
AddStandardResilienceHandler(options => ...) |
HttpClient.Timeout
HttpClient.Timeout dotyczy każdego żądania wysyłanego przez wystąpienie HttpClient. Aby użyć innego limitu czasu dla jednego żądania, przekaż element CancellationToken z CancellationTokenSource, który ma własny limit czasu. Krótsze z tych dwóch ma zastosowanie. Ustaw Timeout.InfiniteTimeSpan na wył., aby to wyłączyć.
W przypadku .NET 5 i nowszych, po przekroczeniu limitu czasu zgłaszany jest wyjątek TaskCanceledException z wyjątkiem TimeoutException wewnątrz. We wcześniejszych wersjach .NET Core wyjątek wewnętrzny nie istnieje. W środowisku .NET Framework otrzymasz zamiast tego element HttpRequestException. Aby uzyskać szczegółowe informacje, zobacz HttpClient.Timeout i Wysyłaj żądania HTTP za pomocą klasy HttpClient.
To również oznacza, że TaskCanceledException ktoś anulował żądanie, na przykład użytkownik, który zamknął stronę. Aby odróżnić przekroczenie limitu czasu od anulowania, sprawdź ex.InnerException is TimeoutException albo sprawdź, czy twój własny token został anulowany.
Standardowy program obsługi odporności
AddStandardResilienceHandler() łączy szeregowo ogranicznik szybkości, łączny limit czasu, mechanizm ponawiania, wyłącznik awaryjny i limit czasu próby. Gdy jedna próba trwa dłużej niż 10 sekund, upływ limitu czasu anuluje próbę, a strategia ponawiania próby spróbuje ponownie: do 3 ponownych prób, z wykładniczym opóźnieniem i losowym rozrzutem, począwszy od 2 sekund. Gdy całe żądanie, w tym ponawianie prób, trwa dłużej niż 30 sekund, łączny limit czasu anuluje je, a twój kod otrzymuje TimeoutRejectedException.
TimeoutRejectedException pochodzi od Exception. Nie jest to ani TimeoutException, ani HttpRequestException, więc blok catch (HttpRequestException) go nie przechwyci. Aby uzyskać pełną listę ustawień domyślnych, zobacz Domyślne ustawienia standardowego modułu obsługi odporności.
Na przykład gdy aplikacja .NET 10, która używa ustawień domyślnych standardowej procedury obsługi, wywołuje interfejs API, którego odpowiedź zajmuje od 11 do 15 sekund, po 30 sekundach aplikacja otrzymuje TimeoutRejectedException, a jej blok catch (HttpRequestException) nie wykonuje się.
Jak obsługiwać przekroczenia limitu czasu klienta HttpClient
- Przechwyć oba wyjątki tam, gdzie wywołujesz interfejs API. Jeśli używasz standardowej procedury obsługi, przechwyć
TimeoutRejectedException. WybierzTaskCanceledExceptiondla elementuHttpClient.Timeout. - Odróżnij przekroczenie limitu czasu od anulowania. Traktuj
TaskCanceledExceptionjako limit czasu tylko wtedy, gdy jego wewnętrzny wyjątek toTimeoutException. Gdy dzwoniący anulował, zatrzymaj działanie bez powiadomienia. - Wybierz limity czasu, które pasują do interfejsu API. Jeśli interfejs API często trwa dłużej niż 10 sekund, zmień limit czasu próby i całkowity limit czasu w pliku
AddStandardResilienceHandler(options => ...). - Nie należy ponawiać próby
POSTlubPATCH, chyba że interfejs API sprawi, że będzie to bezpieczne. Standardowy moduł obsługi domyślnie ponawia próbę wszystkich metod, w tymPOST. Wywołajoptions.Retry.DisableForUnsafeHttpMethods(), aby wykluczyćPOST,PATCH,PUT,DELETEiCONNECT, luboptions.Retry.DisableFor(HttpMethod.Post, HttpMethod.Patch), aby nadal ponawiać próby w przypadku idempotentnychPUTiDELETE. - Poinformuj użytkownika o tym, co się stało. Pokaż komunikat "Usługa działa wolno, spróbuj ponownie" zamiast błędu ogólnego.
using Polly.Timeout;
public async Task<string?> GetForecastAsync(HttpClient client, CancellationToken cancellationToken)
{
try
{
return await client.GetStringAsync("https://api.contoso.com/forecast", cancellationToken);
}
catch (TimeoutRejectedException)
{
// Standard resilience handler: total timeout expired after all retries
return null;
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
// HttpClient.Timeout expired
return null;
}
catch (HttpRequestException)
{
// Network error, or an error status code after all retries
return null;
}
}
Jak przetestować, czy aplikacja obsługuje limity czasu
Rzadko widzisz przekroczenie limitu czasu podczas programowania, więc bloki catch rzadko są uruchamiane. Sposób testowania określa, czy znajdziesz błędy, zanim zrobią to użytkownicy.
| Approach | Co znajdziesz | Co tracisz |
|---|---|---|
| Czekaj na produkcję | Rzeczywiste limity czasu | Wszystko, dopóki użytkownik go nie kliknie |
| Mockuj API w testach lub pozwól agentowi kodowania napisać mocka |
catch Czy blok się uruchamia, jeśli mock zgłasza właściwy wyjątek |
HttpClient Rzeczywista konfiguracja, ponawianie prób programu obsługi odporności i wyjątek, który faktycznie zgłasza. Aplikacja wymaga również przełącznika tylko do testowania, aby uzyskać pozorny dostęp. |
| Wywołaj prawdziwy interfejs API i miej nadzieję, że będzie działać wolno | Rzeczywiste zachowanie | Nie można spowolnić interfejsu API w razie potrzeby |
| Przechwytuj rzeczywisty ruch aplikacji i opóźniaj odpowiedzi | Rzeczywisty HttpClientprogram obsługi odporności i wyjątki |
Nic się nie zmienia w aplikacji, więc nie testuje twojego kodu w izolacji. Zostaw testy jednostkowe do tego. |
Wypróbuj ją w aplikacji
Serwer proxy deweloperów przechwytuje żądania aplikacji do interfejsu API i opóźnia odpowiedzi za pomocą parametru LatencyPlugin. .NET używa ustawień proxy systemu, więc nie trzeba zmieniać kodu. Ten przykład opóźnia każdą odpowiedź o 11 do 15 sekund, dłużej niż 10-sekundowy limit czasu próby standardowego modułu obsługi. Zastąp https://api.contoso.com ciąg adresem URL interfejsu API wywoływanego przez aplikację.
Plik: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "LatencyPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "slowApi"
}
],
"urlsToWatch": [
"https://api.contoso.com/*"
],
"slowApi": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/latencyplugin.schema.json",
"minMs": 11000,
"maxMs": 15000
}
}
Uruchom usługę Dev Proxy za pomocą devproxy --config-file devproxyrc.json polecenia i uruchom aplikację. Każda próba kończy się przekroczeniem limitu czasu, moduł obsługi ponawia próbę, a po 30 sekundach kod otrzymuje TimeoutRejectedException. Aby zamiast tego przetestować HttpClient.Timeout, ustaw minMs na wartość wyższą niż skonfigurowany limit czasu. Aby uzyskać szczegółowe informacje na temat konfiguracji, zobacz Use Dev Proxy with .NET applications (Używanie serwera proxy deweloperskiego z aplikacjami .NET). Aby zainstalować Dev Proxy, zobacz Konfigurowanie narzędzia Dev Proxy.