Nello scorso script (https://www.aspitalia.com/script/1294/Ottimizzare-Codice-Richieste-HTTP-Tramite-IHttpClientFactory-ASP.NET-Core-2.1.aspx) abbiamo visto come, grazie alla nuova interfaccia IHttpClientFactory possiamo ridurre la quantità di codice necessaria a inviare una richiesta web.
Tuttavia, la comunicazione con web api di terze parti non è sempre così semplice: il server remoto può restituire un errore oppure possono verificarsi brevi disservizi sulla rete internet. Vediamo come gestire queste situazioni.
Configurare strategie di retry con Polly
Quando si verifica un errore con una richiesta web, è possibile che la causa sia un problema transitorio che scompare subito dopo, semplicemente riprovando a inviare la richiesta. In questa situazione, abbiamo quindi bisogno di un meccanismo di retry automatico o, se proprio il problema non dovesse risolversi neanche dopo alcuni tentativi, dobbiamo quanto meno evitare che gli effetti negativi influenzino la nostra applicazione. Marco De Sanctis ha affrontato questi temi in precedenti script sfruttando Polly ([url]https://www.aspitalia.com/ricerca/super.aspx?key=Polly[/url]), una libreria creata proprio a questo scopo.Con ASP.NET Core 2.1 è diventato ancora più semplice sfruttare Polly grazie alla collaborazione di Microsoft che l'ha reso facilmente integrabile con IHttpClientFactory. Iniziamo installando il pacchetto NuGet Microsoft.Extensions.Http.Polly e poi completiamo la configurazione vista in precedenza.
services.AddHttpClient("RemoteWebApi", client => { //Qui configuriamo l'istanza di HttpClient }) //CIRCUIT BREAKER //Se nelle precedenti esecuzioni si sono verificati 3 errori consecutivi, dobbiamo //supporre che la web api remota non stia funzionando. Allora diamo immediatamente //errore per i prossimi 30 secondi, in modo da non far attendere gli utenti invano. .AddTransientHttpErrorPolicy(builder => builder.CircuitBreakerAsync(3, TimeSpan.FromSeconds(30))) //RETRY //Se si verificano errori o riceviamo status code 5xx o 408 (request timeout) //allora riproviamo per altre 2 volte prima di rinunciare .AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[] { //Qui definiamo le attese tra un tentativo e l'altro TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2) }));
Come abbiamo appena visto, usando l'extension method AddTransientHttpErrorPolicy da interfaccia fluente possiamo aggiungere alla pipeline uno o più message handler di Polly per attuare strategie di retry avanzate con la minima quantità di codice. Volendo, possiamo anche attuare logiche personalizzate scrivendo un nostro message handler.
Creare un message handler personalizzato
In questa dimostrazione, scriviamo un message handler che faccia il logging delle richieste in uscita, misurandone il tempo di esecuzione e lo status code risultante. Potremo poi usare queste informazioni per attestare l'affidabilità del servizio. Creiamo ora una nuova classe che derivi da DelegatingHandler.public class LogMessageHandler : DelegatingHandler { private readonly ILogger logger; //Sfruttiamo la dependecy injection public LogMessageHandler(ILoggerFactory loggerFactory) { logger = loggerFactory.CreateLogger<LogMessageHandler>(); } protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { //La richiesta deve ancora essere inviata; avviamo il timer var stopwatch = new Stopwatch(); stopwatch.Start(); //Invochiamo il prossimo message handler nella pipeline var response = await base.SendAsync(request, cancellationToken); //Qui la richiesta è stata inviata, misuriamo il tempo impiegato stopwatch.Stop(); var elapsed = stopwatch.ElapsedMilliseconds; logger.LogInformation($"La richiesta a {request.RequestUri} ha impiegato {elapsed}ms" + " e si è conclusa con lo status code {response.StatusCode}"); return response; } }
Ora possiamo aggiungere il message handler alla pipeline usando l'extension method AddHttpMessageHandler.
services.AddHttpClient("RemoteWebApi", client => { //Qui eventuale inizializzazione del client client.BaseAddress = new Uri("http://example.com/api"); client.DefaultRequestHeaders.Add("Authorization", "ApiKey valore"); }) .AddHttpMessageHandler<LogMessageHandler>(); //Inoltre, registriamolo come servizio services.AddTransient<LogMessageHandler>();
Conclusioni
Ora possiamo sfruttare la stessa flessibilità della pipeline di ASP.NET Core anche per le richieste in uscita grazie a IHttpClientFactory. Sfruttando l'integrazione con Polly, con poco codice possiamo realizzare applicazioni più resistenti ai problemi transitori di rete.Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Creare applicazioni distribuite con Azure Container Apps e Dapr
Utilizzare il browser per rilevare Javascript e CSS non utilizzati nel codice
Gestire dati sensibili nella configurazione in ASP.NET Core
Gestire la query string nell'output cache di ASP.NET Core
Log streaming di una Azure Container App
Gestire tipi complessi in query string grazie a IParsable in ASP.NET Core 7.0
Eliminare spazio inutilizzato in un Azure Container Registry
Utilizzare .NET Framework con le Azure Function in modalità isolata
YARP: un reverse proxy in ASP.NET Core
Organizzare il codice JavaScript utilizzando i moduli
Utilizzare la parola chiave nameof per referenziare i nomi dei parametri di un metodo in C#
I più letti di oggi
- Sfruttare la local cache del browser tramite gli ETag in #aspnetcore https://aspit.co/cfc di @crad77 #webapi #aspnetmvc #blazor #cache
- Annunciati .NET 2015, ASP.NET 5 e Visual Studio 2015: open source, per Windows, Linux e MacOSX
- Sottoscrizione agli eventi sul contenitore in JavaScript
- Catturare la telemetria degli eventi di output cache in ASP.NET Core