Quando in Blazor eseguiamo una chiamata HTTP, ci troviamo in un contesto di particolare fragilità, dato che l'esecuzione potrebbe non andare a buon fine per molteplici ragioni: il server potrebbe essere giù, la connessione di rete potrebbe non essere disponibile, o magari ci viene restuito uno status code 500 (Internal Server Error) a causa di un bug sul nostro web service.
Sfortunatamente in Blazor non esiste un gestore delle eccezioni centralizzato, quindi l'unica alternativa che abbiamo è crearci un wrapper di HttpClient tramite cui effettuare le chiamate in maniera controllata.
Una semplice implementazione potrebbe essere quella del codice in basso:
public class SafeHttpClient { private HttpClient _client; private IModalService _modal; public SafeHttpClient(HttpClient client, IModalService modal) { _client = client; _modal = modal; } public async Task<T> ExecuteAsync<T>(Func<HttpClient, Task<T>> httpCall) { try { return await httpCall(_client); } catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.InternalServerError) { Console.WriteLine($"Error intercepted {e.StatusCode}"); var parameters = new ModalParameters(); parameters.Add(nameof(SimpleMessage.Message), "Il server ha restituito un errore, si consiglia di riprovare più tardi"); _modal.Show<SimpleMessage>("Errore durante la chiamata di rete", parameters); return default(T); } } }
Questa classe espone un metodo ExecuteAsync a cui possiamo passare un delegate asincrono, ossia un frammento di codice che accetti in ingresso un HttpClient e restituisca un generico Task<T>.
La logica è banale, e si limita semplicemente a effettuare la chiamata all'interno di un blocco Try...Catch. Nel caso di InternalServerError, il codice sfrutta il componente Blazored.Modal (https://github.com/Blazored/Modal) per mostrare una popup con un generico messaggio di errore.
La finestra modale si basa su un componente SimpleMessage.razor che accetta un parametro per il messaggio da mostrare:
<div> <p>@Message</p> <button @onclick="Cancel" class="btn btn-primary">Ok</button> </div> @code { [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } [Parameter] public string Message { get; set; } async Task Cancel() => await BlazoredModal.CancelAsync(); }
Come al solito, dobbiamo ricordarci di registrare i componenti necessari nell'IoC container di Blazor:
public static async Task Main(string[] args) { // ... altro codice qui ... builder.Services.AddHttpClient("default", httpclient => { httpclient.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); }); builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("default")); builder.Services.AddBlazoredModal(); builder.Services.AddTransient<SafeHttp>(); ... await builder.Build().RunAsync(); }
Se abbimo svolto tutti i passaggi correttamente, possiamo modificare la pagina FetchData.razor del template di esempio di Blazor e sfruttare la nostra nuova classe SafeHttpClient in questo modo:
@page "/fetchdata" @inject SafeHttpClient SafeHttpClient ... @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await SafeHttpClient.ExecuteAsync( http => http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast")); } }
Una nota da fare riguarda il fatto che l'extension method GetFromJsonAsync tramite cui effettuiamo la chiamata, verifica al suo interno il successo della stessa invocando EnsureSuccessStatusCode di HttpResponseMessage; quest'ultimo solleva una HttpRequestException nel caso in cui ci sia stato un errore. Se invece abbiamo utilizzato i metodi GetAsync, PostAsync, e via discorrendo, dobbiamo ricordarci di chiamare manualmente EnsureSuccessStatusCode, perché altrimenti non verrà sollevata alcuna eccezione in caso di errore dal server.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Fornire parametri ad un Web component HTML
Creare una libreria CSS universale: Nav menu
Generare velocemente pagine CRUD in Blazor con QuickGrid
Documentare i servizi REST con Swagger e OpenAPI con .NET 9
Triggerare una pipeline su un altro repository di Azure DevOps
Migliorare la sicurezza dei prompt con Azure AI Studio
Fissare una versione dell'agent nelle pipeline di Azure DevOps
Utilizzare WhenEach per processare i risultati di una lista di task
Creare un webhook in Azure DevOps
Usare le navigation property in QuickGrid di Blazor
Ottimizzare le pull con Artifact Cache di Azure Container Registry
Generare un hash con SHA-3 in .NET