Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
När ett API är för långsamt får din .NET-app 1 av 2 undantag, beroende på vilken timeout som utlöses.
HttpClient.Timeout kastar en TaskCanceledException. Standardhanteraren för resiliens från Microsoft.Extensions.Http.Resilience genererar en TimeoutRejectedException från Polly. De kommer från olika platser, har olika standardvärden och behöver separata catch block.
Vilken timeout utlöstes?
| Tidsavbrott | Standardvärde | Vad din kod får | Där du ställer in den |
|---|---|---|---|
HttpClient.Timeout |
100 sekunder |
TaskCanceledException, med en TimeoutException som InnerException (.NET 5 och senare) |
HttpClient.Timeout |
| Tidsgräns för standardhanterarförsök | 10 sekunder per försök | Ingenting först: hanteraren gör ett nytt försök | AddStandardResilienceHandler(options => ...) |
| Total tidsgräns för standardhanterare | 30 sekunder, inklusive alla återförsök | Polly.Timeout.TimeoutRejectedException |
AddStandardResilienceHandler(options => ...) |
HttpClient.Timeout
HttpClient.Timeout gäller för varje begäran som HttpClient-instansen skickar. Om du vill använda en annan timeout för en enda begäran skickar du en CancellationToken från en CancellationTokenSource med en egen timeout. Den kortare av de två gäller. Ange Timeout.InfiniteTimeSpan för att stänga av den.
På .NET 5 och senare kastar en timeout en TaskCanceledException med en TimeoutException inuti. I äldre versioner av .NET Core finns inte det inre undantaget. På .NET Framework får du en HttpRequestException i stället. Mer information finns i HttpClient.Timeout och Gör HTTP-begäranden med klassen HttpClient.
En TaskCanceledException innebär också att någon avbröt begäran, till exempel en användare som stängde sidan. Om du vill skilja en timeout från en annullering kontrollerar du ex.InnerException is TimeoutException eller kontrollerar om din egen token har avbrutits.
Standardhanteraren för resiliens
AddStandardResilienceHandler() kedjar en frekvensbegränsare, en total timeout, ett återförsök, en kretsbrytare och en timeout för ett försök. När ett försök tar längre tid än 10 sekunder avbryts det av tidsgränsen för försöket och återförsöksstrategin försöker igen: upp till 3 återförsök, med exponentiell backoff och jitter, från och med 2 sekunder. När hela begäran, inklusive återförsök, tar längre tid än 30 sekunder avbryter den totala tidsgränsen den och koden får en TimeoutRejectedException.
TimeoutRejectedException härleds från Exception. Det är varken en TimeoutException eller ett HttpRequestException, så ett catch (HttpRequestException) block fångar det inte. Den fullständiga listan över standardvärden finns i Standardvärden för standardhanteraren för återhämtning.
När till exempel en .NET 10-app som använder standardhanterarens standardinställningar anropar ett API som tar 11 till 15 sekunder per svar, får appen en TimeoutRejectedException efter 30 sekunder och dess catch (HttpRequestException)-block körs inte.
Hantera HttpClient-timeouter
- Fånga båda undantagen där du anropar API:et. Om du använder standardhanteraren, fånga
TimeoutRejectedException. HämtaTaskCanceledExceptionförHttpClient.Timeout. - Skilj en timeout från en avbrytning. Behandla endast en
TaskCanceledExceptionsom en timeout när dess inre undantag är enTimeoutException. När anroparen avbröt, avsluta tyst. - Välj tidsgränser som passar API:et. Om API:et ofta tar längre tid än 10 sekunder ändrar du tidsgränsen för försök och den totala tidsgränsen i
AddStandardResilienceHandler(options => ...). - Försök inte igen
POSTellerPATCHsåvida inte API:et gör det säkert. Standardhanteraren försöker som standard alla metoder igen, inklusivePOST. Anropaoptions.Retry.DisableForUnsafeHttpMethods()för att exkluderaPOST,PATCH,PUT,DELETEochCONNECT, elleroptions.Retry.DisableFor(HttpMethod.Post, HttpMethod.Patch)för att fortsätta göra nya försök att utföra idempotentaPUTochDELETE. - Berätta för användaren vad som hände. Visa "tjänsten är seg, försök igen" i stället för ett allmänt fel.
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;
}
}
Så här testar du att din app hanterar timeoutar
Du ser sällan att en tidsgräns överskrids när du utvecklar, så dina catch-block körs nästan aldrig. Hur du testar avgör om du hittar buggarna innan användarna gör det.
| Approach | Det här hittar du | Vad du saknar |
|---|---|---|
| Vänta på produktionsmiljön | Realtidsgränser | Allt, tills en användare trycker på den |
| Mocka API:et i dina tester eller låt kodningsagenten skriva en mock | Huruvida ditt catch-block körs och om mocken genererar rätt undantag |
Din verkliga HttpClient konfiguration, resilienshanterarens återförsök och undantaget som den faktiskt utlöser. Din app behöver också en testinställning för att nå mocken. |
| Anropa det verkliga API:et och hoppas att det går långsamt | Faktiskt beteende | Du kan inte göra API:et långsamt när du vill |
| Fånga upp appens verkliga trafik och fördröja svaren | Din verkliga HttpClient, resilience-hanterare och undantag |
Ingenting i din app ändras, så den testar inte din kod isolerat. Spara det till enhetstesterna. |
Prova det i din app
Dev Proxy fångar upp appens begäranden till API:et och fördröjer svaren med LatencyPlugin. .NET använder systemproxyn, så du behöver inte ändra koden. Det här exemplet fördröjer varje svar med 11 till 15 sekunder, längre än standardhanterarens tidsgräns för försök på 10 sekunder. Ersätt https://api.contoso.com med URL:en för API:et som appen anropar.
Fil: 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
}
}
Starta Dev Proxy med devproxy --config-file devproxyrc.json och kör din app. Varje försök överskrider tidsgränsen, hanteraren försöker igen och efter 30 sekunder får din kod ett TimeoutRejectedException. Testa HttpClient.Timeout i stället genom att ange minMs högre än den tidsgräns som du har konfigurerat. Mer information om konfiguration finns i Använd Dev Proxy med .NET-program. Om du vill installera Dev Proxy, se Konfigurera Dev Proxy.