Gérer les erreurs dans les applications ASP.NET Core Blazor
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 8 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 8 de cet article.
Cet article décrit comment Blazor gère les exceptions non prises en charge et comment développer des applications qui détectent et gèrent les erreurs.
Erreurs détaillées pendant le développement
Lorsqu’une application Blazor ne fonctionne pas correctement pendant le développement, la réception d’informations détaillées sur les erreurs de l’application aide à diagnostiquer et résoudre le problème. Lorsqu’une erreur se produit, les applications Blazor affichent une barre jaune clair en bas de l’écran :
- Pendant le développement, la barre vous dirige vers la console du navigateur, où vous pouvez voir l’exception.
- En production, la barre avertit l’utilisateur qu’une erreur s’est produite et recommande d’actualiser le navigateur.
L’interface utilisateur de cette expérience de gestion des erreurs fait partie des modèles de projet Blazor. Les versions des modèles de projet Blazor n’utilisent pas toutes l’attribut data-nosnippet
pour signaler aux navigateurs de ne pas mettre en cache le contenu de l’interface utilisateur des erreurs, mais toutes les versions de la documentation Blazor appliquent l’attribut.
Dans une Blazor Web App, personnalisez l’expérience dans le composant MainLayout
. Étant donné que le Assistance de balise d’environnement (par exemple, <environment include="Production">...</environment>
) n’est pas pris en charge dans les composants Razor , l’exemple suivant injecte IHostEnvironment pour configurer des messages d’erreur pour différents environnements.
En haut de MainLayout.razor
:
@inject IHostEnvironment HostEnvironment
Créez ou modifiez le balisage de l’interface utilisateur d’erreur Blazor :
<div id="blazor-error-ui" data-nosnippet>
@if (HostEnvironment.IsProduction())
{
<span>An error has occurred.</span>
}
else
{
<span>An unhandled exception occurred.</span>
}
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
Dans une application Blazor Server, personnalisez l’expérience dans le fichier Pages/_Host.cshtml
. L’exemple suivant utilise l' Assistance de balise d’environnement pour configurer les messages d’erreur pour différents environnements.
Dans une application Blazor Server, personnalisez l’expérience dans le fichier Pages/_Layout.cshtml
. L’exemple suivant utilise l' Assistance de balise d’environnement pour configurer les messages d’erreur pour différents environnements.
Dans une application Blazor Server, personnalisez l’expérience dans le fichier Pages/_Host.cshtml
. L’exemple suivant utilise l' Assistance de balise d’environnement pour configurer les messages d’erreur pour différents environnements.
Créez ou modifiez le balisage de l’interface utilisateur d’erreur Blazor :
<div id="blazor-error-ui" data-nosnippet>
<environment include="Staging,Production">
An error has occurred.
</environment>
<environment include="Development">
An unhandled exception occurred.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
Dans une application Blazor WebAssembly, personnalisez l’expérience dans le fichier wwwroot/index.html
:
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
L’élément blazor-error-ui
est normalement masqué en raison de la présence du style display: none
de la classe CSS blazor-error-ui
dans la feuille de style générée automatiquement de l’application. Lorsqu’une erreur se produit, le framework applique display: block
à l’élément.
L’élément blazor-error-ui
est normalement masqué en raison de la présence du style display: none
de la classe CSS blazor-error-ui
dans la feuille de style du site dans le dossier wwwroot/css
. Lorsqu’une erreur se produit, le framework applique display: block
à l’élément.
Erreurs de circuit détaillées
Cette section s’applique aux Blazor Web App fonctionnant sur un circuit.
Cette section s’applique aux applications Blazor Server.
Les erreurs côté client n’incluent pas la pile des appels et ne fournissent pas de détails sur la cause de l’erreur, mais les journaux du serveur contiennent de telles informations. À des fins de développement, des informations d’erreur de circuit sensibles peuvent être mises à la disposition du client en activant les erreurs détaillées.
Affectez la valeur CircuitOptions.DetailedErrors à true
. Pour plus d’informations et un exemple, consultez le Guide pour ASP.NET Core BlazorSignalR.
Une alternative à la définition de CircuitOptions.DetailedErrors consiste à définir la clé de configuration DetailedErrors
sur true
dans le fichier des paramètres d’environnement de Development
de l’application (appsettings.Development.json
). En outre, définissez la journalisation côté serveur SignalR (Microsoft.AspNetCore.SignalR
) sur Déboguer ou Trace pour une journalisation détaillée SignalR.
appsettings.Development.json
:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}
La clé de configuration DetailedErrors peut également être définie sur true
à l’aide de la variable d’environnement ASPNETCORE_DETAILEDERRORS
avec une valeur de true
sur des serveurs d’environnement de Development
/Staging
ou sur votre système local.
Avertissement
Évitez toujours d’exposer des informations d’erreur aux clients sur Internet, ce qui constitue un risque de sécurité.
Erreurs détaillées pour le rendu côté serveur du composant Razor
Cette section s’applique aux Blazor Web App.
Utilisez l’option RazorComponentsServiceOptions.DetailedErrors pour contrôler la production d’informations détaillées sur les erreurs pour le rendu côté serveur du composant Razor. La valeur par défaut est false
.
L’exemple suivant active les erreurs détaillées :
builder.Services.AddRazorComponents(options =>
options.DetailedErrors = builder.Environment.IsDevelopment());
Avertissement
Activez uniquement les erreurs détaillées dans l’environnement de Development
. Des erreurs détaillées peuvent contenir des informations sensibles sur l’application que les utilisateurs malveillants peuvent utiliser dans une attaque.
L’exemple précédent fournit un degré de sécurité en définissant la valeur de DetailedErrors en fonction de la valeur retournée par IsDevelopment. Lorsque l’application se trouve dans l’environnement Development
, DetailedErrors est définie sur true
. Cette approche n’est pas infaillible, car il est possible d’héberger une application de production sur un serveur public dans l’environnement Development
.
Gérer les exceptions non prises en charge dans le code du développeur
Pour qu’une application continue après une erreur, elle doit avoir une logique de gestion des erreurs. Les sections ultérieures de cet article décrivent les sources potentielles d’exceptions non prises en charge.
En production, ne restituez pas les messages d’exception de framework ou les traces de pile dans l’interface utilisateur. Le rendu des messages d’exception ou des traces de pile peut :
- Divulguer des informations sensibles aux utilisateurs finaux.
- Aider un utilisateur malveillant à découvrir les faiblesses d’une application qui peuvent compromettre la sécurité de l’application, du serveur ou du réseau.
Exceptions non gérées pour les circuits
Cette section s’applique aux applications Web côté serveur fonctionnant sur un circuit.
Razor composants avec l’interactivité du serveur activée sont avec état sur le serveur. Bien que les utilisateurs interagissent avec le composant sur le serveur, ils conservent une connexion au serveur appelé circuit. Le circuit contient des instances de composants actifs, ainsi que de nombreux autres aspects de l’état, comme :
- La sortie rendue la plus récente des composants.
- L’ensemble actuel de délégués de gestion des événements qui peuvent être déclenchés par des événements côté client.
Si un utilisateur ouvre l’application dans plusieurs onglets de navigateur, il crée plusieurs circuits indépendants.
Blazor traite la plupart des exceptions non prises en charge comme irrécupérables pour le circuit où elles se produisent. Si un circuit est arrêté en raison d’une exception non prise en charge, l’utilisateur peut uniquement continuer à interagir avec l’application en rechargeant la page pour créer un nouveau circuit. Les circuits en dehors de celui qui est terminé, qui sont des circuits pour d’autres utilisateurs ou d’autres onglets de navigateur, ne sont pas affectés. Ce scénario est similaire à une application de bureau qui se bloque. L’application plantée doit être redémarrée, mais les autres applications ne sont pas affectées.
Le framework met fin à un circuit lorsqu’une exception non prise en charge se produit pour les raisons suivantes :
- Une exception non prise en charge laisse souvent le circuit dans un état non défini.
- Le fonctionnement normal de l’application ne peut pas être garanti après une exception non prise en charge.
- Des vulnérabilités de sécurité peuvent apparaître dans l’application si le circuit continue dans un état non défini.
Gestion globale des exceptions
Pour connaître les approches de gestion globale des exceptions, consultez les sections suivantes :
- Limites d’erreur : s’applique à toutes les applications Blazor.
- Autre gestion globale des exceptions : s’applique aux Blazor Server, Blazor WebAssembly et Blazor Web App (8.0 ou versions ultérieures) qui adoptent un mode de rendu interactif global.
Limites d’erreur
Les limites d’erreur fournissent une approche pratique pour la gestion des exceptions. Le composant ErrorBoundary :
- Affiche son contenu enfant lorsqu’une erreur ne s’est pas produite.
- Affiche l’interface utilisateur d’erreur lorsqu’une exception non prise en charge est levée par n’importe quel composant dans la limite d’erreur.
Pour définir une limite d’erreur, utilisez le composant ErrorBoundary pour encapsuler un ou plusieurs autres composants. La limite d’erreur gère les exceptions non gérées levées par les composants qu’elle encapsule.
<ErrorBoundary>
...
</ErrorBoundary>
Pour implémenter une limite d’erreur de manière globale, ajoutez la limite autour du contenu du corps du layout principal de l’application.
Dans MainLayout.razor
:
<article class="content px-4">
<ErrorBoundary>
@Body
</ErrorBoundary>
</article>
Dans les Blazor Web App avec la limite d’erreur appliquée uniquement à un composant MainLayout
statique, la limite est active uniquement pendant la phase de rendu statique côté serveur (SSR statique). La limite ne s’active pas juste parce qu’un composant plus bas dans la hiérarchie des composants est interactif.
Un mode de rendu interactif ne peut pas être appliqué au composant MainLayout
, car le paramètre Body
du composant est un délégué RenderFragment, qui est un code arbitraire et ne peut pas être sérialisé. Pour permettre une interactivité à grande échelle pour le composant MainLayout
et le rest des composants situés plus bas dans la hiérarchie des composants, l’application doit adopter un mode de rendu interactif global en appliquant le mode de rendu interactif aux instances de HeadOutlet
et Routes
dans le composant racine de l’application, qui est généralement le composant App
. L’exemple suivant adopte globalement le mode de rendu de serveur interactif (InteractiveServer
).
Dans Components/App.razor
:
<HeadOutlet @rendermode="InteractiveServer" />
...
<Routes @rendermode="InteractiveServer" />
Si vous préférez ne pas activer l’interactivité globale, placez la limite d’erreur plus loin dans la hiérarchie des composants. Les concepts importants à garder à l’esprit sont que chaque fois que la limite d’erreur est placée :
- Si le composant où se trouve la limite d’erreur n’est pas interactif, la limite d’erreur peut uniquement s’activer sur le serveur pendant le SSR statique. Par exemple, la limite peut s’activer lorsqu’une erreur est levée dans une méthode de cycle de vie de composant, mais pas pour un événement déclenché par l’interactivité utilisateur au sein du composant, par exemple une erreur levée par un gestionnaire de clics de bouton.
- Si le composant où se trouve la limite d’erreur est interactif, la limite d’erreur peut s’activer pour les composants interactifs qu’elle encapsule.
Remarque
Les considérations précédentes ne sont pas pertinentes pour les applications Blazor WebAssembly autonomes, car le rendu côté client d’une application Blazor WebAssembly est complètement interactif.
Prenons l’exemple suivant, où une exception levée par un composant de compteur incorporé est interceptée par une limite d’erreur dans le composant Home
, qui adopte un mode de rendu interactif.
EmbeddedCounter.razor
:
<h1>Embedded Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
if (currentCount > 5)
{
throw new InvalidOperationException("Current count is too big!");
}
}
}
Home.razor
:
@page "/"
@rendermode InteractiveServer
<PageTitle>Home</PageTitle>
<h1>Home</h1>
<ErrorBoundary>
<EmbeddedCounter />
</ErrorBoundary>
Prenons l’exemple suivant, où une exception levée par un composant de compteur incorporé est interceptée par une limite d’erreur dans le composant Home
.
EmbeddedCounter.razor
:
<h1>Embedded Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
if (currentCount > 5)
{
throw new InvalidOperationException("Current count is too big!");
}
}
}
Home.razor
:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Home</h1>
<ErrorBoundary>
<EmbeddedCounter />
</ErrorBoundary>
Si l’exception non prises en charge est levée pour un currentCount
de plus de cinq :
- L’erreur est enregistrée normalement (
System.InvalidOperationException: Current count is too big!
). - L’exception est gérée par la limite d’erreur.
- L’interface utilisateur d’erreur par défaut est rendue par la limite d’erreur.
Le composant ErrorBoundary affiche un élément <div>
vide avec la classe CSS blazor-error-boundary
pour son contenu d’erreur. Les couleurs, le texte et l’icône de l’interface utilisateur par défaut sont définis dans la feuille de style de l’application dans le dossier wwwroot
. Vous êtes donc libre de personnaliser l’interface utilisateur d’erreur.
Pour modifier le contenu d’erreur par défaut :
- Encapsulez les composants de la limite d’erreur dans la propriété ChildContent.
- Définissez la propriété ErrorContent sur le contenu d’erreur.
L’exemple suivant encapsule le composant EmbeddedCounter
et fournit le contenu d’erreur personnalisé :
<ErrorBoundary>
<ChildContent>
<EmbeddedCounter />
</ChildContent>
<ErrorContent>
<p class="errorUI">😈 A rotten gremlin got us. Sorry!</p>
</ErrorContent>
</ErrorBoundary>
Pour l’exemple précédent, la feuille de style de l’application inclut vraisemblablement une classe CSS errorUI
pour mettre en forme le contenu. Le contenu d’erreur est rendu à partir de la propriété ErrorContent sans élément de niveau bloc. Un élément de niveau bloc, tel qu’un élément de division (<div>
) ou de paragraphe (<p>
), peut encapsuler le balisage de contenu d’erreur, mais il n’est pas nécessaire.
Si vous le souhaitez, utilisez le contexte (@context
) de l’objet ErrorContent pour obtenir les données d’erreur :
<ErrorContent>
@context.HelpLink
</ErrorContent>
L’objet ErrorContent peut également nommer le contexte. Dans l’exemple suivant, le contexte est nommé exception
:
<ErrorContent Context="exception">
@exception.HelpLink
</ErrorContent>
Avertissement
Évitez toujours d’exposer des informations d’erreur aux clients sur Internet, ce qui constitue un risque de sécurité.
Si la limite d’erreur est définie dans la disposition de l’application, l’interface utilisateur d’erreur est visible, quelle que soit la page vers laquelle l’utilisateur accède après l’erreur. Dans la plupart des scénarios, nous vous recommandons de définir étroitement les limites d’erreur. Si vous limitez largement une limite d’erreur, vous pouvez la réinitialiser à un état non d’erreur sur les événements de navigation de page suivants en appelant la méthode Recover de la limite d’erreur.
Dans MainLayout.razor
:
- Ajoutez un champ pour le ErrorBoundary à capturer une référence à celle-ci avec la directive d’attribut
@ref
. - Dans la méthode de cycle de vie
OnParameterSet
, vous pouvez déclencher une récupération au niveau de la limite de l’erreur avec Recover afin d’effacer l’erreur lorsque l’utilisateur accède à un autre composant.
...
<ErrorBoundary @ref="errorBoundary">
@Body
</ErrorBoundary>
...
@code {
private ErrorBoundary? errorBoundary;
protected override void OnParametersSet()
{
errorBoundary?.Recover();
}
}
Pour éviter la boucle infinie où la récupération revient simplement à réactiver un composant qui lève à nouveau l’erreur, n’appelez pas Recover de la logique de rendu. Appelez uniquement Recover lorsque :
- L’utilisateur effectue un mouvement d’interface utilisateur, par exemple en sélectionnant un bouton pour indiquer qu’il souhaite réessayer une procédure ou lorsque l’utilisateur accède à un nouveau composant.
- Une logique supplémentaire qui s’exécute efface également l’exception. Lorsque le composant est remangé, l’erreur ne se reproduit pas.
L’exemple suivant permet à l’utilisateur de se libérer de l’exception à l’aide d’un bouton :
<ErrorBoundary @ref="errorBoundary">
<ChildContent>
<EmbeddedCounter />
</ChildContent>
<ErrorContent>
<div class="alert alert-danger" role="alert">
<p class="fs-3 fw-bold">😈 A rotten gremlin got us. Sorry!</p>
<p>@context.HelpLink</p>
<button class="btn btn-info" @onclick="_ => errorBoundary?.Recover()">
Clear
</button>
</div>
</ErrorContent>
</ErrorBoundary>
@code {
private ErrorBoundary? errorBoundary;
}
Vous pouvez également utiliser une sous-classe pour ErrorBoundary pour un traitement personnalisé en remplaçant OnErrorAsync. L’exemple suivant consigne simplement l’erreur, mais vous pouvez implémenter le code de gestion des erreurs de votre choix. Vous pouvez supprimer la ligne qui renvoie une CompletedTask si votre code attend une tâche asynchrone.
CustomErrorBoundary.razor
:
@inherits ErrorBoundary
@inject ILogger<CustomErrorBoundary> Logger
@if (CurrentException is null)
{
@ChildContent
}
else if (ErrorContent is not null)
{
@ErrorContent(CurrentException)
}
@code {
protected override Task OnErrorAsync(Exception ex)
{
Logger.LogError(ex, "😈 A rotten gremlin got us. Sorry!");
return Task.CompletedTask;
}
}
L’exemple précédent peut également être implémenté en tant que classe.
CustomErrorBoundary.cs
:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace BlazorSample;
public class CustomErrorBoundary : ErrorBoundary
{
[Inject]
ILogger<CustomErrorBoundary> Logger { get; set; } = default!;
protected override Task OnErrorAsync(Exception ex)
{
Logger.LogError(ex, "😈 A rotten gremlin got us. Sorry!");
return Task.CompletedTask;
}
}
L’une des implémentations précédentes utilisées dans un composant :
<CustomErrorBoundary>
...
</CustomErrorBoundary>
Gestion globale des exceptions alternative
L’approche décrite dans cette section s’applique aux Blazor Server, Blazor WebAssembly et Blazor Web App qui adoptent un mode de rendu interactif global (InteractiveServer
, InteractiveWebAssembly
ou InteractiveAuto
). L’approche ne fonctionne pas avec les Blazor Web App qui adoptent des modes de rendu par page ou par composant, ou un rendu statique côté serveur (SSR statique), car l’approche s’appuie sur un CascadingValue
/CascadingParameter
, qui ne fonctionne pas sur les limites du mode de rendu ou avec les composants qui adoptent le SSR statique.
Une alternative à l’utilisation des limites d’erreur (ErrorBoundary) consiste à passer un composant d’erreur personnalisé en tant que CascadingValue
aux composants enfants. L’un des avantages de l’utilisation d’un composant par rapport à l’utilisation d’un service injecté ou d’une implémentation d’enregistreur d’événements personnalisé est qu’un composant en cascade peut restituer du contenu et appliquer des styles CSS en cas d’erreur.
L’exemple de composant ProcessError
suivant journalise simplement les erreurs, mais les méthodes du composant peuvent traiter les erreurs de n’importe quelle façon requise par l’application, notamment en utilisant plusieurs méthodes de traitement des erreurs.
ProcessError.razor
:
@inject ILogger<ProcessError> Logger
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
public void LogError(Exception ex)
{
Logger.LogError("ProcessError.LogError: {Type} Message: {Message}",
ex.GetType(), ex.Message);
// Call StateHasChanged if LogError directly participates in
// rendering. If LogError only logs or records the error,
// there's no need to call StateHasChanged.
//StateHasChanged();
}
}
Remarque
Pour plus d’informations sur RenderFragment, consultez Composants ASP.NET Core Razor.
Lorsque vous utilisez cette approche dans une Blazor Web App, ouvrez le composant Routes
et encapsulez le composant Router (<Router>...</Router>
) avec le composant ProcessError
. Cela permet au composant ProcessError
de descendre en cascade vers n’importe quel composant de l’application où le composant ProcessError
est reçu en tant que CascadingParameter
.
Dans Routes.razor
:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Lorsque vous utilisez cette approche dans une application Blazor Server ou Blazor WebAssembly, ouvrez le composant App
, encapsulez le composant Router (<Router>...</Router>
) avec le composant ProcessError
. Cela permet au composant ProcessError
de descendre en cascade vers n’importe quel composant de l’application où le composant ProcessError
est reçu en tant que CascadingParameter
.
Dans App.razor
:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Pour traiter les erreurs dans un composant :
Désignez le composant
ProcessError
en tant queCascadingParameter
dans le bloc@code
. Dans un exemple de composantCounter
dans une application basée sur un modèle de projet Blazor, ajoutez la propriétéProcessError
suivante :[CascadingParameter] public ProcessError? ProcessError { get; set; }
Appelez une méthode de traitement des erreurs dans n’importe quel bloc
catch
avec un type d’exception approprié. L’exemple de composantProcessError
n’offre qu’une seule méthodeLogError
, mais le composant de traitement des erreurs peut fournir un nombre quelconque de méthodes de traitement des erreurs pour répondre à d’autres exigences de traitement des erreurs dans l’application. L’exemple de block@code
du composantCounter
suivant inclut le paramètre en cascadeProcessError
et intercepte une exception pour la journalisation lorsque le nombre est supérieur à cinq :@code { private int currentCount = 0; [CascadingParameter] public ProcessError? ProcessError { get; set; } private void IncrementCount() { try { currentCount++; if (currentCount > 5) { throw new InvalidOperationException("Current count is over five!"); } } catch (Exception ex) { ProcessError?.LogError(ex); } } }
Erreur journalisée :
fail: {COMPONENT NAMESPACE}.ProcessError[0]
ProcessError.LogError: System.InvalidOperationException Message: Current count is over five!
Si la méthode LogError
participe directement au rendu, comme l’affichage d’une barre de messages d’erreur personnalisée ou la modification des styles CSS des éléments rendus, appelez StateHasChanged
à la fin de la méthode LogError
pour réactiver l’interface utilisateur.
Étant donné que les approches de cette section gèrent les erreurs avec une instruction try-catch
, la connexion d’une application SignalR entre le client et le serveur n’est pas interrompue lorsqu’une erreur se produit, et le circuit reste actif. Les autres exceptions non prises en charge restent irrécupérables pour un circuit. Pour plus d’informations, consultez la section sur comment un circuit réagit aux exceptions non gérées.
Une application peut utiliser un composant de traitement des erreurs comme valeur en cascade pour traiter les erreurs de manière centralisée.
Le composant ProcessError
suivant se transmet lui-même en tant que CascadingValue
aux composants enfants. L’exemple journalise simplement l’erreur, mais les méthodes du composant peuvent traiter les erreurs de n’importe quelle façon requise par l’application, notamment en utilisant plusieurs méthodes de traitement des erreurs. L’un des avantages de l’utilisation d’un composant par rapport à l’utilisation d’un service injecté ou d’une implémentation d’enregistreur d’événements personnalisé est qu’un composant en cascade peut restituer du contenu et appliquer des styles CSS en cas d’erreur.
ProcessError.razor
:
@using Microsoft.Extensions.Logging
@inject ILogger<ProcessError> Logger
<CascadingValue Value="this">
@ChildContent
</CascadingValue>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
public void LogError(Exception ex)
{
Logger.LogError("ProcessError.LogError: {Type} Message: {Message}",
ex.GetType(), ex.Message);
}
}
Remarque
Pour plus d’informations sur RenderFragment, consultez Composants ASP.NET Core Razor.
Dans le composant App
, encapsulez le composant Router avec le composant ProcessError
. Cela permet au composant ProcessError
de descendre en cascade vers n’importe quel composant de l’application où le composant ProcessError
est reçu en tant que CascadingParameter
.
App.razor
:
<ProcessError>
<Router ...>
...
</Router>
</ProcessError>
Pour traiter les erreurs dans un composant :
Désignez le composant
ProcessError
en tant queCascadingParameter
dans le bloc@code
:[CascadingParameter] public ProcessError ProcessError { get; set; }
Appelez une méthode de traitement des erreurs dans n’importe quel bloc
catch
avec un type d’exception approprié. L’exemple de composantProcessError
n’offre qu’une seule méthodeLogError
, mais le composant de traitement des erreurs peut fournir un nombre quelconque de méthodes de traitement des erreurs pour répondre à d’autres exigences de traitement des erreurs dans l’application.try { ... } catch (Exception ex) { ProcessError.LogError(ex); }
À l’aide de l’exemple précédent de composant ProcessError
et de la LogError
méthode, la console des outils de développement du navigateur indique l’erreur interceptée et journalisée :
fail: {COMPONENT NAMESPACE}.Shared.ProcessError[0]
ProcessError.LogError: System.NullReferenceException Message: Object reference not set to an instance of an object.
Si la méthode LogError
participe directement au rendu, comme l’affichage d’une barre de messages d’erreur personnalisée ou la modification des styles CSS des éléments rendus, appelez StateHasChanged
à la fin de la méthode LogError
pour réactiver l’interface utilisateur.
Étant donné que les approches de cette section gèrent les erreurs avec une instruction try-catch
, la connexion SignalR d’une application Blazor entre le client et le serveur n’est pas interrompue lorsqu’une erreur se produit, et le circuit reste actif. Toute exception non prise en charge est irrécupérable pour un circuit. Pour plus d’informations, consultez la section sur comment un circuit réagit aux exceptions non gérées.
Erreurs de journalisation avec un fournisseur persistant
Si une exception non gérée se produit, l’exception est enregistrée dans les instances ILogger configurées dans le conteneur de service. Les applications Blazor consignent dans la sortie de la console à l’aide du fournisseur de journalisation de la console. Envisagez de vous connecter à un emplacement sur le serveur (ou l’API web principale pour les applications côté client) avec un fournisseur qui gère la taille des journaux et la rotation des journaux. L’application peut également utiliser un service de gestion des performances des applications (APM), comme Azure Application Insights (Azure Monitor).
Remarque
Les fonctionnalités de Application Insights natives pour prendre en charge les applications côté client et la prise en charge native de Blazor framework pour Google Analytics peuvent devenir disponibles dans les futures versions de ces technologies. Pour plus d’informations, consultez Prise en charge d’App Insights dans Blazor WASM côté client (microsoft/ApplicationInsights-dotnet #2143) et Analyse web et diagnostics (comprend des liens vers des implémentations de la communauté) (dotnet/aspnetcore #5461). En attendant, une application côté client peut utiliser le SDK JavaScript Application Insights avec JSinteropérabilité pour consigner des erreurs directement dans Application Insights à partir d’une application côté client.
Pendant le développement dans une application Blazor fonctionnant sur un circuit, l’application envoie généralement les détails complets des exceptions à la console du navigateur pour faciliter le débogage. En production, les erreurs détaillées ne sont pas envoyées aux clients, mais les détails complets d’une exception sont enregistrés sur le serveur.
Vous devez déterminer quels incidents journaliser et le niveau de gravité des incidents journalisés. Des utilisateurs hostiles pourraient être en mesure de déclencher des erreurs délibérément. Par exemple, ne journalisez pas d’incident à partir d’une erreur où un ProductId
inconnu est fourni dans l’URL d’un composant qui affiche les détails du produit. Toutes les erreurs ne doivent pas être traitées comme des incidents pour la journalisation.
Pour plus d’informations, consultez les articles suivants :
- Journalisation ASP.NET Core Blazor
- Gérer les erreurs dans ASP.NET Core‡
- Créer des API web avec ASP.NET Core
‡S’applique aux applications Blazor côté serveur et à d’autres applications ASP.NET Core côté serveur qui sont des applications back-end d’API web pour Blazor. Les applications côté client peuvent intercepter et envoyer des informations d’erreur sur le client à une API web, qui enregistre les informations d’erreur sur un fournisseur de journalisation persistant.
Si une exception non gérée se produit, l’exception est enregistrée dans les instances ILogger configurées dans le conteneur de service. Les applications Blazor consignent dans la sortie de la console à l’aide du fournisseur de journalisation de la console. Envisagez de journaliser vers un emplacement plus permanent sur le serveur en envoyant des informations d’erreur à une API web back-end qui utilise un fournisseur de journalisation avec la gestion de la taille des journaux et la rotation des journaux. L’application d’API web back-end peut également utiliser un service de gestion des performances des applications (APM), comme Azure Application Insights (Azure Monitor)†, pour enregistrer les informations d’erreur qu’elle reçoit des clients.
Vous devez déterminer quels incidents journaliser et le niveau de gravité des incidents journalisés. Des utilisateurs hostiles pourraient être en mesure de déclencher des erreurs délibérément. Par exemple, ne journalisez pas d’incident à partir d’une erreur où un ProductId
inconnu est fourni dans l’URL d’un composant qui affiche les détails du produit. Toutes les erreurs ne doivent pas être traitées comme des incidents pour la journalisation.
Pour plus d’informations, consultez les articles suivants :
- Journalisation ASP.NET Core Blazor
- Gérer les erreurs dans ASP.NET Core‡
- Créer des API web avec ASP.NET Core
†Les fonctionnalités de Application Insights natives pour prendre en charge les applications côté client et la prise en charge native de Blazor framework pour Google Analytics peuvent devenir disponibles dans les futures versions de ces technologies. Pour plus d’informations, consultez Prise en charge d’App Insights dans Blazor WASM côté client (microsoft/ApplicationInsights-dotnet #2143) et Analyse web et diagnostics (comprend des liens vers des implémentations de la communauté) (dotnet/aspnetcore #5461). En attendant, une application côté client peut utiliser le SDK JavaScript Application Insights avec JSinteropérabilité pour consigner des erreurs directement dans Application Insights à partir d’une application côté client.
‡S’applique aux applications ASP.NET Core côté serveur qui sont des applications back-end d’API web pour les applications Blazor. Les applications côté client interceptent et envoient des informations d’erreur à une API web, qui consigne les informations d’erreur à un fournisseur de journalisation persistant.
Emplacements où des erreurs peuvent se produire
Le code de framework et d’application peut déclencher des exceptions non prises en charge dans l’un des emplacements suivants, qui sont décrits plus en détail dans les sections suivantes de cet article :
Instanciation de composant
Quand Blazor crée une instance d’un composant :
- Le constructeur du composant est appelé.
- Les constructeurs des services de DI fournis au constructeur du composant via la directive
@inject
ou l’attribut[Inject]
sont appelés.
Une erreur dans un constructeur ou un setter exécuté pour toute propriété [Inject]
entraîne une exception non prise en charge et empêche le framework d’instancier le composant. Si l’application fonctionne sur un circuit, le circuit échoue. Si la logique du constructeur peut lever des exceptions, l’application doit intercepter les exceptions à l’aide d’une instruction try-catch
avec gestion des erreurs et journalisation.
Méthodes de cycle de vie
Pendant la durée de vie d’un composant, Blazor appelle des méthodes de cycle de vie. Si une méthode de cycle de vie lève une exception, de manière synchrone ou asynchrone, l’exception est irrécupérable pour un circuit. Pour que les composants traitent les erreurs dans les méthodes de cycle de vie, ajoutez une logique de gestion des erreurs.
Dans l’exemple suivant, où OnParametersSetAsync appelle une méthode pour obtenir un produit :
- Une exception levée dans la méthode
ProductRepository.GetProductByIdAsync
est gérée par une instructiontry-catch
. - Lorsque le bloc
catch
est exécuté :loadFailed
est défini surtrue
, qui est utilisé pour afficher un message d’erreur à l’utilisateur.- L’erreur est journalisée.
@page "/product-details/{ProductId:int?}"
@inject ILogger<ProductDetails> Logger
@inject IProductRepository Product
<PageTitle>Product Details</PageTitle>
<h1>Product Details Example</h1>
@if (details != null)
{
<h2>@details.ProductName</h2>
<p>
@details.Description
<a href="@details.Url">Company Link</a>
</p>
}
else if (loadFailed)
{
<h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
<h1>Loading...</h1>
}
@code {
private ProductDetail? details;
private bool loadFailed;
[Parameter]
public int ProductId { get; set; }
protected override async Task OnParametersSetAsync()
{
try
{
loadFailed = false;
// Reset details to null to display the loading indicator
details = null;
details = await Product.GetProductByIdAsync(ProductId);
}
catch (Exception ex)
{
loadFailed = true;
Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
}
}
public class ProductDetail
{
public string? ProductName { get; set; }
public string? Description { get; set; }
public string? Url { get; set; }
}
/*
* Register the service in Program.cs:
* using static BlazorSample.Components.Pages.ProductDetails;
* builder.Services.AddScoped<IProductRepository, ProductRepository>();
*/
public interface IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id);
}
public class ProductRepository : IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id)
{
return Task.FromResult(
new ProductDetail()
{
ProductName = "Flowbee ",
Description = "The Revolutionary Haircutting System You've Come to Love!",
Url = "https://flowbee.com/"
});
}
}
}
@page "/product-details/{ProductId:int?}"
@inject ILogger<ProductDetails> Logger
@inject IProductRepository Product
<PageTitle>Product Details</PageTitle>
<h1>Product Details Example</h1>
@if (details != null)
{
<h2>@details.ProductName</h2>
<p>
@details.Description
<a href="@details.Url">Company Link</a>
</p>
}
else if (loadFailed)
{
<h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
<h1>Loading...</h1>
}
@code {
private ProductDetail? details;
private bool loadFailed;
[Parameter]
public int ProductId { get; set; }
protected override async Task OnParametersSetAsync()
{
try
{
loadFailed = false;
// Reset details to null to display the loading indicator
details = null;
details = await Product.GetProductByIdAsync(ProductId);
}
catch (Exception ex)
{
loadFailed = true;
Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
}
}
public class ProductDetail
{
public string? ProductName { get; set; }
public string? Description { get; set; }
public string? Url { get; set; }
}
/*
* Register the service in Program.cs:
* using static BlazorSample.Components.Pages.ProductDetails;
* builder.Services.AddScoped<IProductRepository, ProductRepository>();
*/
public interface IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id);
}
public class ProductRepository : IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id)
{
return Task.FromResult(
new ProductDetail()
{
ProductName = "Flowbee ",
Description = "The Revolutionary Haircutting System You've Come to Love!",
Url = "https://flowbee.com/"
});
}
}
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository
@if (details != null)
{
<h1>@details.ProductName</h1>
<p>@details.Description</p>
}
else if (loadFailed)
{
<h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
<h1>Loading...</h1>
}
@code {
private ProductDetail? details;
private bool loadFailed;
[Parameter]
public int ProductId { get; set; }
protected override async Task OnParametersSetAsync()
{
try
{
loadFailed = false;
// Reset details to null to display the loading indicator
details = null;
details = await ProductRepository.GetProductByIdAsync(ProductId);
}
catch (Exception ex)
{
loadFailed = true;
Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
}
}
public class ProductDetail
{
public string? ProductName { get; set; }
public string? Description { get; set; }
}
public interface IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id);
}
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository
@if (details != null)
{
<h1>@details.ProductName</h1>
<p>@details.Description</p>
}
else if (loadFailed)
{
<h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
<h1>Loading...</h1>
}
@code {
private ProductDetail? details;
private bool loadFailed;
[Parameter]
public int ProductId { get; set; }
protected override async Task OnParametersSetAsync()
{
try
{
loadFailed = false;
// Reset details to null to display the loading indicator
details = null;
details = await ProductRepository.GetProductByIdAsync(ProductId);
}
catch (Exception ex)
{
loadFailed = true;
Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
}
}
public class ProductDetail
{
public string? ProductName { get; set; }
public string? Description { get; set; }
}
public interface IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id);
}
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository
@if (details != null)
{
<h1>@details.ProductName</h1>
<p>@details.Description</p>
}
else if (loadFailed)
{
<h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
<h1>Loading...</h1>
}
@code {
private ProductDetail details;
private bool loadFailed;
[Parameter]
public int ProductId { get; set; }
protected override async Task OnParametersSetAsync()
{
try
{
loadFailed = false;
// Reset details to null to display the loading indicator
details = null;
details = await ProductRepository.GetProductByIdAsync(ProductId);
}
catch (Exception ex)
{
loadFailed = true;
Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
}
}
public class ProductDetail
{
public string ProductName { get; set; }
public string Description { get; set; }
}
public interface IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id);
}
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository
@if (details != null)
{
<h1>@details.ProductName</h1>
<p>@details.Description</p>
}
else if (loadFailed)
{
<h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
<h1>Loading...</h1>
}
@code {
private ProductDetail details;
private bool loadFailed;
[Parameter]
public int ProductId { get; set; }
protected override async Task OnParametersSetAsync()
{
try
{
loadFailed = false;
// Reset details to null to display the loading indicator
details = null;
details = await ProductRepository.GetProductByIdAsync(ProductId);
}
catch (Exception ex)
{
loadFailed = true;
Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
}
}
public class ProductDetail
{
public string ProductName { get; set; }
public string Description { get; set; }
}
public interface IProductRepository
{
public Task<ProductDetail> GetProductByIdAsync(int id);
}
}
Logique de rendu
Le balisage déclaratif dans un fichier de composant Razor (.razor
) est compilé dans une méthode C# appelée BuildRenderTree. Lorsqu’un composant effectue un rendu, BuildRenderTree exécute et génère une structure de données décrivant les éléments, le texte et les composants enfants du composant rendu.
La logique de rendu peut lever une exception. Un exemple de ce scénario se produit lorsque @someObject.PropertyName
est évalué, mais que @someObject
est null
. Pour Blazor applications fonctionnant sur un circuit, une exception non gérée levée par la logique de rendu est irrécupérable pour le circuit de l’application.
Pour empêcher une NullReferenceException dans une logique de rendu, recherchez un objet null
avant d’accéder à ses membres. Dans l’exemple suivant, les propriétés person.Address
ne sont pas accessibles si person.Address
est null
:
@if (person.Address != null)
{
<div>@person.Address.Line1</div>
<div>@person.Address.Line2</div>
<div>@person.Address.City</div>
<div>@person.Address.Country</div>
}
Le code précédent suppose que person
n’est pas null
. Souvent, la structure du code garantit qu’un objet existe au moment où le composant est rendu. Dans ces cas, il n’est pas nécessaire de vérifier null
dans la logique de rendu. Dans l’exemple précédent, l’existence de person
peut être garantie, car person
est créé lorsque le composant est instancié, comme l’illustre l’exemple suivant :
@code {
private Person person = new();
...
}
Gestionnaires d’événements
Le code côté client déclenche des appels de code C# lorsque des gestionnaires d’événements sont créés avec :
@onclick
@onchange
- D’autres attributs
@on...
@bind
Le code du gestionnaire d’événements peut lever une exception non prise en charge dans ces scénarios.
Si l’application appelle du code susceptible d’échouer pour des raisons externes, interceptez les exceptions à l’aide d’une instruction try-catch
avec gestion et journalisation des erreurs.
Si un gestionnaire d’événements lève une exception non prise en charge (par exemple, si une requête de base de données échoue) qui n’est pas interceptée et gérée par le code du développeur :
- Le framework consigne l’exception.
- Dans une application Blazor fonctionnant sur un circuit, l’exception est irrécupérable pour le circuit de l’application.
Mise au rebut des supports
Un composant peut être supprimé de l’interface utilisateur, par exemple parce que l’utilisateur a accédé à une autre page. Lorsqu’un composant qui implémente System.IDisposable est supprimé de l’interface utilisateur, le framework appelle la méthode Dispose du composant.
Si la méthode Dispose
du composant lève une exception non gérée dans une application Blazor fonctionnant sur un circuit, l’exception est irrécupérable pour le circuit de l’application.
Si la logique de suppression peut lever des exceptions, l’application doit intercepter les exceptions à l’aide d’une instruction try-catch
avec gestion des erreurs et journalisation.
Pour plus d’informations sur la suppression de composants, consultez Cycle de vie des composants ASP.NET Core Razor.
Interopérabilité JavaScript
IJSRuntime est inscrit par le framework Blazor. IJSRuntime.InvokeAsync permet au code .NET d’effectuer des appels asynchrones au runtime JavaScript (JS) dans le navigateur de l’utilisateur.
Les conditions suivantes s’appliquent à la gestion des erreurs avec InvokeAsync :
- Si un appel à InvokeAsync échoue de façon synchrone, une exception .NET se produit. Un appel à InvokeAsync peut échouer, par exemple si les arguments fournis ne peuvent pas être sérialisés. Le code du développeur doit intercepter l’exception. Si le code d’application d’un gestionnaire d’événements ou d’une méthode de cycle de vie de composant ne gère pas une exception dans une application Blazor fonctionnant sur un circuit, l’exception résultante est irrécupérable pour le circuit de l’application.
- Si un appel à InvokeAsync échoue de façon asynchrone, la Task .NET échoue. Un appel à InvokeAsync peut échouer, par exemple, car le code côté JS lève une exception ou retourne une
Promise
qui s’est terminée en tant querejected
. Le code du développeur doit intercepter l’exception. Si vous utilisez l’opérateurawait
, envisagez d’encapsuler l’appel de méthode dans une instructiontry-catch
avec la gestion des erreurs et la journalisation. Dans le cas contraire, dans une application Blazor fonctionnant sur un circuit, le code défaillant entraîne une exception non gérée irrécupérable pour le circuit de l’application. - Les appels vers InvokeAsync doivent être complétés dans un délai défini, sinon l’appel expire. Le délai d’expiration par défaut est d’une minute. Le délai d’expiration protège le code contre la perte de connectivité réseau ou le code JS qui ne renvoie jamais de message d’achèvement. Si l’appel expire, la System.Threading.Tasks résultante échoue avec une OperationCanceledException. Interceptez et traitez l’exception avec journalisation.
De même, le code JS peut lancer des appels aux méthodes .NET indiquées par l’attribut [JSInvokable]
. Si ces méthodes .NET lèvent une exception non prise en charge :
- Dans une application Blazor fonctionnant dans un circuit, l’exception n’est pas considérée comme irrécupérable pour le circuit de l’application.
- La
Promise
côté JS est rejetée.
Vous avez la possibilité d’utiliser le code de gestion des erreurs côté .NET ou côté JS de l’appel de méthode.
Pour plus d’informations, consultez les articles suivants :
- Appeler des fonctions JavaScript à partir de méthodes .NET dans ASP.NET Core Blazor
- Appeler des méthodes .NET à partir de fonctions JavaScript dans ASP.NET Core Blazor
Prérendu
Razor composants sont pré-affichés par défaut afin que leur balisage HTML rendu soit retourné dans le cadre de la requête HTTP initiale de l’utilisateur.
Dans une application Blazor fonctionnant sur un circuit, le pré-affichage fonctionne par :
- Création d’un nouveau circuit pour tous les composants prérendus qui font partie de la même page.
- Génération du HTML initial.
- Traitement du circuit comme
disconnected
jusqu’à ce que le navigateur de l’utilisateur établisse une connexion SignalR au même serveur. Une fois la connexion établie, l’interactivité sur le circuit reprend et le balisage HTML des composants est mis à jour.
Pour les composants côté client pré-affichés, le pré-affichage fonctionne par :
- Générant le code HTML initial sur le serveur pour tous les composants prérendus qui font partie de la même page.
- Rendant le composant interactif sur le client une fois que le navigateur a chargé le code compilé de l’application et le runtime .NET (s’il n’est pas déjà chargé) en arrière-plan.
Si un composant lève une exception non prise en charge pendant le prérendu, par exemple, au cours d’une méthode de cycle de vie ou dans une logique de rendu :
- Dans une application Blazor fonctionnant sur un circuit, l’exception est irrécupérable pour le circuit. Pour les composants côté client prédéfinis, l’exception empêche le rendu du composant.
- L’exception est levée dans la pile des appels à partir du ComponentTagHelper.
Dans des circonstances normales où le prérendu échoue, la poursuite de la génération et du rendu du composant n’a pas de sens, car un composant de travail ne peut pas être rendu.
Pour tolérer les erreurs qui peuvent se produire pendant le prérendu, la logique de gestion des erreurs doit être placée à l’intérieur d’un composant qui peut lever des exceptions. Utilisez des instructions try-catch
avec la gestion et la journalisation des erreurs. Au lieu d’encapsuler le ComponentTagHelper dans une instruction try-catch
, placez la logique de gestion des erreurs dans le composant rendu par le ComponentTagHelper.
Scénarios avancés
Rendu récursif
Les composants peuvent être imbriqués récursivement. Cela est utile pour représenter des structures de données récursives. Par exemple, un composant TreeNode
peut afficher davantage de composants TreeNode
pour chacun des enfants du nœud.
Lors d’un rendu récursif, évitez de coder des modèles qui entraînent une récursivité infinie :
- Ne restituez pas de manière récursive une structure de données qui contient un cycle. Par exemple, ne restituez pas un nœud d’arborescence dont les enfants s’incluent eux-mêmes.
- Ne créez pas de chaîne de dispositions qui contiennent un cycle. Par exemple, ne créez pas de dispositions dont la disposition est elle-même.
- N’autorisez pas un utilisateur final à enfreindre les invariants de récursion (règles) par le biais d’une entrée de données malveillante ou d’appels d’interopérabilité JavaScript.
Les boucles infinies pendant le rendu :
- Entraînent la poursuite indéfinie du processus de rendu.
- Équivalent à créer une boucle non déterminée.
Dans ces scénarios, le Blazor échoue et tente généralement de :
- Consommer autant de temps processeur que le système d’exploitation le permet, indéfiniment.
- Consommer une quantité illimitée de mémoire. La consommation de mémoire illimitée équivaut au scénario où une boucle non déterminée ajoute des entrées à une collection à chaque itération.
Pour éviter les cas de récursivité infinie, assurez-vous que le code de rendu récursif contient des conditions d’arrêt appropriées.
Logique d’arborescence de rendu personnalisée
La plupart des composants Razor sont implémentés en tant que fichiers de composants Razor (.razor
) et sont compilés par l’infrastructure pour produire une logique qui fonctionne sur un RenderTreeBuilder pour afficher leur sortie. Toutefois, un développeur peut implémenter manuellement une logique RenderTreeBuilder à l’aide de code C# procédural. Pour plus d’informations, consultez Scénarios avancés ASP.NET Core Blazor (construction d’arborescence de rendu).
Warning
L’utilisation de la logique de générateur d’arborescences de rendu manuel est considérée comme un scénario avancé et dangereux, non recommandé pour le développement de composants généraux.
Si du code RenderTreeBuilder est écrit, le développeur doit garantir son exactitude. Par exemple, le développeur doit s’assurer que :
- Les appels à OpenElement et CloseElement sont correctement équilibrés.
- Les attributs sont ajoutés uniquement aux emplacements appropriés.
Une logique de rendu manuel de générateur d’arborescences incorrecte peut entraîner un comportement arbitraire non défini, tel que des plantages, l’application ou le serveur qui cesse de répondre, et des vulnérabilités de sécurité.
Considérez la logique de générateur d’arborescences de rendu manuel au même niveau de complexité et avec le même niveau de danger que l’écriture manuelle de code d’assembly ou d’instructions Microsoft Intermediate Language (MSIL).
Ressources supplémentaires
†S’applique aux applications d’API web ASP.NET Core que les applications Blazor côté client utilisent pour la journalisation.