Fermer

septembre 12, 2022

Meilleures pratiques Blazor : gestion des erreurs


Des erreurs sont susceptibles de se produire, la question est de savoir comment les gérer. Jetons un coup d’œil aux meilleures pratiques pour les gérer dans votre application Blazor.

Tôt ou tard, quelque chose ne va pas dans votre application Blazor.

Qu’il s’agisse d’une entrée inattendue, d’un cas marginal que vous n’avez pas anticipé ou de la mise hors service de votre base de données par votre hébergeur pour maintenance. Des erreurs sont susceptibles de se produire, la question est de savoir comment les gérer.

Il y a un certain nombre de facteurs ici, y compris :

  • Comment gérer au mieux les erreurs, afin que les utilisateurs ne soient pas obligés de regarder un écran cassé
  • Comment diagnostiquer et résoudre le problème en cas de problème
  • Tactiques que vous pouvez utiliser pour vous assurer que vous êtes conscient des problèmes de production
  • Mesures que vous pouvez prendre pour éviter ou gérer les erreurs prévisibles

Si vous créez des applications Web Blazor, vous disposez de quelques options pour relever ces défis. Nous allons jeter un coup d’oeil.

Erreurs lors du développement

Le premier endroit où détecter les erreurs est lorsque vous créez votre application Blazor. Vous verrez des erreurs légèrement différentes selon le modèle d’hébergement que vous utilisez (serveur ou WASM).

Erreurs du serveur Blazor pendant le développement

Si ton Serveur Blazor rencontre un problème pendant le développement, vous verrez probablement un message vous dirigeant vers la console du navigateur pour plus de détails.

Fenêtre du navigateur affichant le texte d'erreur par défaut du serveur Blazor Avertissement concernant une exception non gérée

La console vous montrera plus de détails, mais si vous voulez vraiment creuser le problème, vous voudrez vous diriger vers votre serveur pour voir des erreurs plus détaillées.

Si vous exécutez localement à l’aide du serveur Web ASP.NET intégré, vous avez très probablement une fenêtre de terminal ouverte quelque part qui ressemble un peu à ceci :

Fenêtre de terminal avec texte décrivant les détails de l'exception non gérée, y compris la trace de la pile

Si vous utilisez Visual Studio, vous trouverez également ces informations dans le Sortie > Débogage la fenêtre.

Il est juste de dire qu’il y a pas mal de bruit dans cette fenêtre ! Mais si vous faites défiler jusqu’à la première erreur signalée, vous trouverez généralement des détails qui peuvent vous aider à localiser le problème.

Dans ce cas, il signale une exception système lancée à la ligne 48 de FetchData.razor.

System.Exception: Oof, an error occurred
   at BestPractices.BlazorServer.Pages.FetchData.OnInitializedAsync() in C:\Users\hilto\RiderProjects\Telerik\BestPractices\BestPractices.BlazorServer\Pages\FetchData.razor:line 48
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost: Error: Unhandled exception in circuit '9s8O3FNORDmRpW-IDAWcj_20c8obFpFxAaMBzPaH-xg'.

Effectivement, il y a notre problème.

FetchData.razor

...

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        throw new Exception("Oof, an error occurred");        
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Erreurs Blazor WASM pendant le développement

Vous verrez quelque chose de similaire dans votre application Blazor WASM, mais avec un libellé légèrement différent.

An unhandled error has occurred. Reload

Étant donné que les composants Blazor s’exécutent dans le navigateur, vous verrez automatiquement des erreurs plus détaillées/directement utiles dans la console du navigateur.

Fenêtre du navigateur affichant le texte d'erreur Blazor WASM par défaut, avertissant d'une exception non gérée

Dans ce cas, l’erreur se trouve à Index.razor, ligne 15.

Garder les lumières allumées, utiliser des limites d’erreur

Si ton Serveur Blazor app génère une erreur (voir ci-dessus), cela empêche effectivement vos utilisateurs de continuer, jusqu’à ce qu’ils rechargent l’application.

Blazor WASM est un peu plus indulgent et vous permettra d’ignorer l’erreur et de continuer.

Quoi qu’il en soit, vous souhaiterez probablement fournir des informations plus conviviales et / ou utiles à vos utilisateurs en cas de problème.

Une façon d’y parvenir consiste à utiliser des limites d’erreur. Vous pouvez envelopper une partie de votre application dans une limite d’erreur, offrant un moyen pratique d’attraper et de gérer les exceptions sans mettre toute votre application à genoux.

Prenons cet exemple, où nous avons une page qui affiche un ProductDetails composant, plus la norme Counter composant.

@page "https://www.telerik.com/"

<ProductDetails />
<Counter />

Voici comment cela Détails du produit aspect du composant :

<h3>ProductDetails</h3>

<button @onclick="BreakMe">Break me!</button>

@code {

    private void BreakMe()
    {
        throw new Exception("I'm broken!");
    }

}

Si nous exécutons ceci et cliquons sur le bouton, nous obtiendrons cette barre d’erreur standard que nous avons vue plus tôt et, dans le cas de Blazor Server, notre application se retrouvera dans un état « cassé », nécessitant un rechargement pour se remettre en marche.

Pour améliorer cette expérience pour nos utilisateurs, nous pouvons envelopper le ProductDetails composant dans un ErrorBoundary.

<ErrorBoundary>
	<ProductDetails />
</ErrorBoundary>

Avec cela en place, lorsque nous cliquons sur le bouton, l’exception se produit toujours mais est gérée, et l’interface utilisateur par défaut pour une limite d’erreur est affichée (au même endroit que le composant qui a généré l’erreur).

Composant Blazor avec un avertissement visible sur une partie de l'écran qui

La bonne nouvelle est que cela maintient notre application opérationnelle et limite l’exception à cette partie de l’interface utilisateur. Dans cet exemple, nous pouvons toujours naviguer vers d’autres pages et/ou interagir avec le widget de compteur sur cette page, même dans Blazor Server (où cela aurait généralement mis le circuit dans un état cassé et empêché l’utilisateur de continuer).

Par défaut, le ErrorBoundary composant rendra un div avec le blazor-error-boundary Classe CSS lorsqu’une erreur se produit.

Vous pouvez remplacer cela et fournir votre propre interface utilisateur personnalisée en définissant un ErrorContent propriété.

<ErrorBoundary>
    <ChildContent>
        <ProductDetails/>
    </ChildContent>
    <ErrorContent>
        <div class="border p-4 text-danger">
            Sorry about that, something seems to have gone awry.
        </div>
    </ErrorContent>
</ErrorBoundary>

Si vous souhaitez approfondir les limites d’erreur, consultez cet article utile sur Travailler avec des exceptions non gérées par Dave Brock.

Anticiper et gérer les erreurs

Tout ce dont nous avons parlé jusqu’à présent se situe dans le contexte des exceptions non gérées.

Si vous choisissez de gérer une exception, vous pouvez bien sûr appliquer la logique que vous jugez appropriée (l’utilisateur n’a jamais besoin de le savoir !). Une stratégie consiste donc à anticiper et à gérer les erreurs à l’aide d’un langage C# standard try/catch approcher.

Par exemple, dans notre ProductDetails Par exemple, nous pourrions essayer de récupérer les détails du produit et de gérer les erreurs qui pourraient survenir.

<h3>ProductDetails</h3>

@if (loadFailed)
{
    <h2>Something went wrong fetching product details</h2>
}

<button @onclick="BreakMe">Break me!</button>

@code {

    bool loadFailed = false;

    private void BreakMe()
    {
        try
        {
            loadFailed = false;
            // load product details here
            throw new Exception("Ooof");
        }
        catch (Exception e)
        {
            loadFailed = true;
        }
    }
}

En pratique, vous ne voulez probablement pas tout envelopper try/catch blocs et vous pouvez toujours utiliser des limites d’erreur pour intercepter les erreurs à la place.

Mais, si vous avez des exceptions spécifiques que vous souhaitez gérer, alors l’humble try/catch est une bonne option à avoir.

Réessayer les requêtes réseau

Nous avons tendance à supposer que les réseaux sont fiables et qu’un appel à une API backend fonctionnera toujours, mais dans la pratique, des problèmes transitoires peuvent survenir lors de l’interaction avec les serveurs, et les serveurs peuvent devenir temporairement indisponibles (en particulier lorsque les déploiements sont poussés ou que des ajustements faite au réseau qui héberge le serveur).

Pour ces raisons, il est avantageux de supposer que vos appels réseau échoueront parfois, en particulier si vous créez une application Blazor WASM où la majeure partie de votre logique backend réside dans une API Web backend.

Vous pouvez adopter l’approche de la force brute en enveloppant manuellement chaque appel backend dans un try/catchmais cela ajoute beaucoup de bruit à votre application et vous laisse toujours face à la question de savoir quoi faire lorsqu’un appel réseau échoue.

Au lieu de cela, pour ce type d’erreurs transitoires, je préfère utiliser Polly dans mes applications Blazor.

Polly est une bibliothèque open-source pour définir des politiques de nouvelle tentative et cela fonctionne bien avec HttpClient pour gérer les pannes transitoires du réseau.

Nous devons d’abord ajouter une référence au package NuGet :

Install-Package Microsoft.Extensions.Http.Polly -Version 6.0.7

Ensuite, dans notre application Blazor WASM, nous devons peaufiner Program.cs utiliser IHttpClientBuilder pour enregistrer un HttpClient et spécifier une stratégie de nouvelle tentative pour les demandes ayant échoué.

Programme.cs

...

builder.Services
    .AddHttpClient("Default",client => client.BaseAddress =  new Uri(builder.HostEnvironment.BaseAddress))
    .AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
    {
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(5)
    }));

Dans ce cas, si une exception se produit lorsque nous effectuons un appel HTTP, Polly réessayera l’appel deux fois, une fois après 1 seconde, et de nouveau après 5. À ce stade, si l’appel backend génère toujours des exceptions, l’exception sera levée. comme d’habitude et notre application réagira en conséquence.

Enfin, nous avons juste besoin de dire à notre application WASM d’utiliser ceci Default HttpClient lorsqu’il demande une instance de HttpClient.

builder.Services.AddScoped(sp => sp.GetService<IHttpClientFactory>().CreateClient("Default"));

Ceci afin de maintenir la rétrocompatibilité. L’application WASM utilisera désormais notre « Par défaut » HttpClientavec politique de nouvelle tentative, chaque fois que nous injectons HttpClient dans un composant.

Diagnostiquer et corriger les erreurs en production

Lorsque des erreurs se produisent en production, elles peuvent être difficiles à identifier ; la clé est d’avoir suffisamment d’informations pour essayer de comprendre/résoudre le problème.

C’est là que les mécanismes de journalisation intégrés d’ASP.NET sont vraiment utiles.

Lorsque vous exécutez votre application (en développement ou en production), vous avez probablement remarqué tous ces messages qui s’affichent dans la console (comme celui que nous avons exploré plus tôt lorsque nous avons rencontré une exception lors du développement).

ASP.NET s’appuie sur quelque chose appelé ILogger pour consigner les informations, les avertissements et les erreurs dans la console par défaut.

Nous pouvons également nous appuyer sur cela en production pour obtenir des journaux d’erreurs détaillés en cas de problème.

Les détails de mise en œuvre varient ici, en fonction de votre infrastructure d’hébergement et vous pouvez choisir de stocker les journaux à plusieurs endroits.

Les destinations de journalisation par défaut pour Blazor Server sont :

  • Console
  • Déboguer
  • Source de l’événement
  • Journal des événements (dans Windows)

Blazor WASM, tel qu’il s’exécute dans le navigateur des utilisateurs, n’a pas accès au système de fichiers ou au réseau du client.

Par conséquent, si vous vous connectez à partir de Blazor WASM, vous êtes plus limité en termes d’endroit où vous pouvez envoyer des journaux, mais votre application est également susceptible d’interagir avec une API backend qui peut enregistrer ses propres journaux.

Avec Server et WASM, vous pouvez envoyer des journaux vers d’autres destinations via des bibliothèques tierces (généralement installées via NuGet), telles que Elmah, Sentry et Serilog, pour n’en nommer que quelques-unes.

Alternativement, vous pouvez aller plus loin et connecter votre application à un service tiers de gestion des performances des applications, tel qu’Azure Application Insights et RayGun, qui vous fourniront souvent des informations beaucoup plus détaillées sur exactement ce qui s’est passé et sur ce qui s’est passé. temps.

Quelle que soit la manière dont vous choisissez de les stocker, vous devrez déterminer le « niveau » de journal à stocker.

Si vous jetez un oeil à votre appsettings.json fichier dans votre application Blazor Server (ou l’API Web backend si vous utilisez Blazor WASM), vous verrez probablement cette configuration :

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

Cela indique qu’en production, le comportement de journalisation par défaut consiste à enregistrer les journaux au niveau « Information » (et au-dessus), à l’exception des erreurs d’ASP.NET Core qui ne seront enregistrées que si elles sont au niveau « Avertissement » ou au-dessus. .

Si vous utilisez Blazor WASM, vous pouvez contrôler ce niveau via Programme.cs.

builder.Logging.SetMinimumLevel(LogLevel.Warning);

En plus de capturer les journaux existants, vous souhaiterez peut-être consigner des informations supplémentaires à partir de vos composants. Vous pouvez le faire en injectant une instance de ILoggerpuis en invoquant son LogX méthodes (où X est le niveau).

ProductDetails.rasoir

@inject ILogger<ProductDetails> Logger

<h3>ProductDetails</h3>
<button @onclick="BreakMe">Break me!</button>
@code {   

    private void BreakMe()
    {
        try
        {            
            
        }
        catch (Exception e)
        {            
            Logger.LogError("Something went wrong retrieving product details");
        }
    }
}

Alertes en cas de problème

Enfin, si vous choisissez de vous appuyer sur une bibliothèque tierce pour la journalisation des erreurs, vous constaterez peut-être qu’elle peut également déclencher des notifications (par exemple, par e-mail) en cas de problème.

Si votre application est cassée, vous voulez probablement le savoir ! Les e-mails d’alerte sont une option utile pour vous assurer que vous savez que quelque chose se passe.

Des outils comme Elmah et Serilog, et des APM comme RayGun et Application Insights peuvent gérer cela pour vous, et également s’assurer que vous n’êtes pas inondé d’e-mails lorsque la même erreur commence à se reproduire plus d’une fois.

En conclusion

Les erreurs font inévitablement partie du développement Web. Blazor, et ASP.NET Core en général, fournissent plusieurs mécanismes pour vous aider à les gérer.

Les limites d’erreur peuvent contenir des exceptions non gérées et maintenir votre application Blazor Server en cours d’exécution même si un composant a échoué.

Que vous utilisiez Blazor WASM ou Server, il est essentiel de savoir ce qui ne va pas en production ou en développement. utilisez les journaux pour vous assurer que vous pouvez creuser dans les détails de toutes les erreurs qui se produisent.

Envisagez des outils tiers pour afficher les journaux et envoyer des e-mails d’alerte en cas de problème, et implémentez une politique de nouvelle tentative simple pour les demandes réseau (afin que votre application puisse résister aux problèmes de réseau transitoires).

Plus de bonnes pratiques Blazor : Chargement des données




Source link

septembre 12, 2022