Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Wanneer een API te traag is, krijgt uw .NET-app 1 van 2 excepties, afhankelijk van welke time-out is geactiveerd.
HttpClient.Timeout gooit een TaskCanceledException. De standaard-resilience-handler van Microsoft.Extensions.Http.Resilience gooit een TimeoutRejectedException van Polly. Ze zijn afkomstig van verschillende plaatsen, hebben verschillende standaardwaarden en hebben afzonderlijke catch blokken nodig.
Welke time-out is geactiveerd
| Onderbreking | Default | Wat je code krijgt | Waar u het instelt |
|---|---|---|---|
HttpClient.Timeout |
100 seconden |
TaskCanceledException, met een TimeoutException als InnerException (.NET 5 en hoger) |
HttpClient.Timeout |
| Time-out voor standaardhandlerpoging | 10 seconden per poging | In eerste instantie gebeurt er niets: de handler probeert het opnieuw | AddStandardResilienceHandler(options => ...) |
| Totale time-out van standaardhandler | 30 seconden, inclusief alle herhalingspogingen | Polly.Timeout.TimeoutRejectedException |
AddStandardResilienceHandler(options => ...) |
HttpClient.Timeout
HttpClient.Timeout is van toepassing op elk verzoek dat door de HttpClient instantie wordt verzonden. Als u voor één aanvraag een andere time-out wilt gebruiken, geeft u een CancellationToken door van een CancellationTokenSource met een eigen time-out. De kortere van de twee is van toepassing. Stel Timeout.InfiniteTimeSpan in om het uit te schakelen.
Op .NET 5 en hoger gooit een time-out een TaskCanceledException met daarin een TimeoutException. In eerdere versies van .NET Core is de interne uitzondering er niet. In .NET Framework krijgt u in plaats daarvan een HttpRequestException. Zie HttpClient.Timeout en HTTP-aanvragen maken met de HttpClient-klasse voor meer informatie.
Een TaskCanceledException betekent ook dat iemand de aanvraag heeft geannuleerd, bijvoorbeeld een gebruiker die de pagina heeft gesloten. Om onderscheid te maken tussen een time-out en een annulering, controleer ex.InnerException is TimeoutException, of controleer of uw eigen token is geannuleerd.
De standaardhandler voor veerkracht
AddStandardResilienceHandler() schakelt een rate limiter, een totale time-out, een herhalingspoging, een circuit breaker en een time-out per poging aaneen. Wanneer een poging langer dan 10 seconden duurt, annuleert de time-out voor die poging deze en probeert de retrystrategie het opnieuw: tot maximaal 3 nieuwe pogingen, met exponentiële backoff en jitter, vanaf 2 seconden. Wanneer het hele verzoek, inclusief nieuwe pogingen, langer duurt dan 30 seconden, annuleert de totale time-out deze en krijgt uw code een TimeoutRejectedException.
TimeoutRejectedException is afgeleid van Exception. Het is noch een TimeoutException, noch een HttpRequestException, dus een catch (HttpRequestException)-blok vangt het niet op. Zie standaardinstellingen voor de standaardresiliencehandler voor de volledige lijst met standaardinstellingen.
Wanneer bijvoorbeeld een .NET 10-app die gebruikmaakt van de standaardinstellingen van de standaardhandler een API aanroept die 11 tot 15 seconden per antwoord nodig heeft, krijgt de app na 30 seconden een TimeoutRejectedException en wordt het catch (HttpRequestException)-blok niet uitgevoerd.
Omgaan met time-outs van HttpClient
- Vang beide uitzonderingen af wanneer u de API aanroept. Als u de standaardhandler gebruikt, onderschept u
TimeoutRejectedException. OntvangTaskCanceledExceptionvoorHttpClient.Timeout. - Maak onderscheid tussen een time-out en een annulering. Beschouw een
TaskCanceledExceptionalleen als een time-out wanneer de interne uitzondering eenTimeoutExceptionis. Wanneer de beller annuleerde, stop stilletjes. - Kies time-outs die passen bij de API. Als de API vaak langer dan 10 seconden duurt, wijzigt u de time-outs voor pogingen en totaal in
AddStandardResilienceHandler(options => ...). - Probeer
POSTofPATCHniet opnieuw, tenzij de API aangeeft dat dit veilig is. De standaardhandler voert standaard alle methoden opnieuw uit, inclusiefPOST. Roepoptions.Retry.DisableForUnsafeHttpMethods()aan omPOST,PATCH,PUT,DELETEenCONNECTuit te sluiten, ofoptions.Retry.DisableFor(HttpMethod.Post, HttpMethod.Patch)om idempotentePUTenDELETEopnieuw te proberen. - Vertel de gebruiker wat er is gebeurd. Toon "de service is traag, probeer het opnieuw" in plaats van een algemene foutmelding.
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;
}
}
Testen of uw app time-outs verwerkt
U ziet zelden een time-out tijdens het ontwikkelen, dus uw catch blokken worden zelden uitgevoerd. De manier waarop u test, bepaalt of u de bugs vindt voordat uw gebruikers dat doen.
| Approach | Wat u vindt | Wat u mist |
|---|---|---|
| Wacht op productie | Real-time time-outs | Alles, tot een gebruiker erop klikt |
| Mock de API in je tests, of laat je codeeragent de mock schrijven | Of uw catch-blok wordt uitgevoerd en of de mock de juiste uitzondering genereert |
Uw echte HttpClient configuratie, de nieuwe pogingen van de resilience-handler en de uitzondering die deze daadwerkelijk genereert. Uw app heeft ook een testswitch nodig om de mock te bereiken. |
| Roep de echte API aan en hoop dat het traag is | Feitelijk gedrag | U kunt de API niet traag maken naar wens |
| Het echte verkeer van uw app onderscheppen en de reacties vertragen | Uw echte HttpClient, resilience-handler en uitzonderingen |
Er verandert niets in uw app, dus uw code wordt niet geïsoleerd getest. Bewaar je unit-tests daarvoor. |
Probeer het in uw app
Dev Proxy onderschept de aanvragen van uw app naar de API en vertraagt de antwoorden met de LatencyPlugin. .NET gebruikt de systeemproxy, dus u hoeft uw code niet te wijzigen. In dit voorbeeld wordt elke reactie met 11 tot 15 seconden vertraagd, langer dan de time-out voor een poging van 10 seconden van de standaardhandler. Vervang https://api.contoso.com door de URL van de API die uw app aanroept.
Bestand: 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
}
}
Start Dev Proxy met devproxy --config-file devproxyrc.json en voer uw app uit. Er treedt een time-out op voor elke poging, de handler probeert het opnieuw en na 30 seconden krijgt uw code een TimeoutRejectedException. Om in plaats daarvan HttpClient.Timeout te testen, stelt u minMs hoger in dan de time-out die u hebt geconfigureerd. Zie Dev Proxy gebruiken met .NET toepassingen voor meer informatie over de installatie. Zie Dev Proxy instellen om Dev Proxy te installeren.