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
Triggerare una pipeline su un altro repository di Azure DevOps
Creare una custom property in GitHub
Referenziare un @layer più alto in CSS
Evitare il flickering dei componenti nel prerender di Blazor 8
Eseguire query manipolando le liste contenute in un oggetto mappato verso una colonna JSON
Utilizzare i primary constructor di C# per inizializzare le proprietà
Popolare una classe a partire dal testo, con Semantic Kernel e ASP.NET Core Web API
Utilizzare QuickGrid di Blazor con Entity Framework
Ottimizzare la latenza in Blazor 8 tramite InteractiveAuto render mode
Sfruttare GPT-4o realtime su Azure Open AI per conversazioni vocali
Sostituire la GitHub Action di login su private registry
Sfruttare MQTT in cloud e in edge con Azure Event Grid