Partager via


Traitement des exceptions non gérées (C#)

par Scott Mitchell

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Lorsqu’une erreur d’exécution se produit sur une application web en production, il est important de notifier un développeur et de consigner l’erreur afin qu’elle puisse être diagnostiqué ultérieurement dans le temps. Ce tutoriel fournit une vue d’ensemble de la façon dont ASP.NET traite les erreurs d’exécution et examine une façon d’exécuter du code personnalisé chaque fois qu’une exception non gérée est en bulle jusqu’au runtime ASP.NET.

Introduction

Lorsqu’une exception non gérée se produit dans une application ASP.NET, elle est en bulle jusqu’au runtime ASP.NET, ce qui déclenche l’événement Error et affiche la page d’erreur appropriée. Il existe trois types de pages d’erreur différents : l’écran jaune d’erreur runtime de mort (YSOD) ; détails de l’exception YSOD ; et les pages d’erreurs personnalisées. Dans le tutoriel précédent, nous avons configuré l’application pour utiliser une page d’erreur personnalisée pour les utilisateurs distants et le YSOD Détails de l’exception pour les utilisateurs qui visitent localement.

L’utilisation d’une page d’erreurs personnalisée conviviale qui correspond à l’apparence du site est préférée à la valeur YSOD d’erreur runtime par défaut, mais l’affichage d’une page d’erreur personnalisée n’est qu’une partie d’une solution de gestion des erreurs complète. Lorsqu’une erreur se produit dans une application en production, il est important que les développeurs soient avertis de l’erreur afin qu’ils puissent déterrer la cause de l’exception et les résoudre. En outre, les détails de l’erreur doivent être consignés afin que l’erreur puisse être examinée et diagnostiqué ultérieurement.

Ce tutoriel montre comment accéder aux détails d’une exception non gérée afin qu’ils puissent être enregistrés et avertis par un développeur. Les deux didacticiels suivants explorent les bibliothèques de journalisation des erreurs qui, après un peu de configuration, avertiront automatiquement les développeurs des erreurs d’exécution et journaliseront leurs détails.

Remarque

Les informations examinées dans ce didacticiel sont les plus utiles si vous devez traiter des exceptions non gérées de manière unique ou personnalisée. Dans les cas où vous devez uniquement consigner l’exception et avertir un développeur, l’utilisation d’une bibliothèque de journalisation des erreurs est le moyen d’y accéder. Les deux didacticiels suivants fournissent une vue d’ensemble de deux bibliothèques de ce type.

Exécution de code lorsque l’événementErrorest déclenché

Les événements fournissent un objet un mécanisme permettant de signaler que quelque chose d’intéressant s’est produit et pour qu’un autre objet exécute du code en réponse. En tant que développeur ASP.NET vous êtes habitué à penser en termes d’événements. Si vous souhaitez exécuter du code lorsque le visiteur clique sur un bouton particulier, vous créez un gestionnaire d’événements pour l’événement de Click ce bouton et placez votre code. Étant donné que le runtime ASP.NET déclenche son Error événement chaque fois qu’une exception non gérée se produit, il suit que le code permettant de journaliser les détails de l’erreur se trouve dans un gestionnaire d’événements. Mais comment créer un gestionnaire d’événements pour l’événement Error ?

L’événement Error est l’un des nombreux événements de la HttpApplication classe qui sont déclenchés à certaines étapes du pipeline HTTP pendant la durée de vie d’une requête. Par exemple, l’événement de BeginRequest la HttpApplication classe est déclenché au début de chaque requête ; son AuthenticateRequest événement est déclenché lorsqu’un module de sécurité a identifié le demandeur. Ces HttpApplication événements donnent au développeur de pages un moyen d’exécuter une logique personnalisée aux différents points de la durée de vie d’une requête.

Les gestionnaires d’événements pour les HttpApplication événements peuvent être placés dans un fichier spécial nommé Global.asax. Pour créer ce fichier dans votre site web, ajoutez un nouvel élément à la racine de votre site web à l’aide du modèle Global Application Class avec le nom Global.asax.

Sceenshot qui met en évidence le fichier Global dot A S A X.

Figure 1 : Ajouter Global.asax à votre application web
(Cliquez pour afficher l’image de taille complète)

Le contenu et la structure du Global.asax fichier créé par Visual Studio diffèrent légèrement selon que vous utilisez un projet d’application web (WAP) ou un projet de site web (WSP). Avec un WAP, l’application Global.asax est implémentée sous la forme de deux fichiers distincts - Global.asax et Global.asax.cs. Le Global.asax fichier ne contient qu’une @Application directive qui référence le .cs fichier ; les gestionnaires d’événements d’intérêt sont définis dans le Global.asax.cs fichier. Pour les fournisseurs de services web, un seul fichier est créé et Global.asaxles gestionnaires d’événements sont définis dans un <script runat="server"> bloc.

Le Global.asax fichier créé dans un waP par le modèle de classe d’application globale de Visual Studio inclut des gestionnaires d’événements nommés Application_BeginRequest, Application_AuthenticateRequestet Application_Error, qui sont des gestionnaires d’événements pour les HttpApplication événements BeginRequest, AuthenticateRequestet Error, respectivement. Il existe également des gestionnaires d’événements nommés Application_Start, Application_EndSession_Start, et Session_End, qui sont des gestionnaires d’événements qui se déclenchent lorsque l’application web démarre, lorsqu’une nouvelle session démarre, lorsque l’application se termine, et lorsqu’une session se termine, respectivement. Le Global.asax fichier créé dans un WSP par Visual Studio contient uniquement les gestionnaires d’événements, les Application_EndSession_Startgestionnaires d’événements, et Session_End les Application_Error. Application_Start

Remarque

Lors du déploiement de l’application ASP.NET, vous devez copier le Global.asax fichier dans l’environnement de production. Le Global.asax.cs fichier, créé dans le WAP, n’a pas besoin d’être copié en production, car ce code est compilé dans l’assembly du projet.

Les gestionnaires d’événements créés par le modèle de classe d’application globale de Visual Studio ne sont pas exhaustifs. Vous pouvez ajouter un gestionnaire d’événements pour n’importe quel HttpApplication événement en nommant le gestionnaire d’événements Application_EventName. Par exemple, vous pouvez ajouter le code suivant au Global.asax fichier pour créer un gestionnaire d’événements pour l’événementAuthorizeRequest :

protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
    // Event handler code
}

De même, vous pouvez supprimer tous les gestionnaires d’événements créés par le modèle de classe d’application globale qui ne sont pas nécessaires. Pour ce didacticiel, nous avons uniquement besoin d’un gestionnaire d’événements pour l’événement Error ; n’hésitez pas à supprimer les autres gestionnaires d’événements du Global.asax fichier.

Remarque

Les modules HTTP offrent un autre moyen de définir des gestionnaires d’événements pour HttpApplication les événements. Les modules HTTP sont créés en tant que fichier de classe qui peut être placé directement dans le projet d’application web ou séparés dans une bibliothèque de classes distincte. Étant donné qu’ils peuvent être séparés dans une bibliothèque de classes, les modules HTTP offrent un modèle plus flexible et réutilisable pour créer HttpApplication des gestionnaires d’événements. Alors que le Global.asax fichier est spécifique à l’application web où il réside, les modules HTTP peuvent être compilés dans des assemblys, auquel cas l’ajout du module HTTP à un site web est aussi simple que la suppression de l’assembly dans le Bin dossier et l’inscription du module dans Web.config. Ce didacticiel n’examine pas la création et l’utilisation de modules HTTP, mais les deux bibliothèques de journalisation des erreurs utilisées dans les deux didacticiels suivants sont implémentées en tant que modules HTTP. Pour plus d’informations sur les avantages des modules HTTP, reportez-vous à l’utilisation de modules et de gestionnaires HTTP pour créer des composants ASP.NET enfichables.

Récupération d’informations sur l’exception non gérée

À ce stade, nous avons un fichier Global.asax avec un gestionnaire d’événements Application_Error . Lorsque ce gestionnaire d’événements s’exécute, nous devons informer un développeur de l’erreur et consigner ses détails. Pour accomplir ces tâches, nous devons d’abord déterminer les détails de l’exception levée. Utilisez la méthode de l’objet GetLastError Server pour récupérer les détails de l’exception non gérée qui a provoqué le déclenchement de l’événementError.

protected void Application_Error(object sender, EventArgs e)
{
    // Get the error details
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;
}

La GetLastError méthode retourne un objet de type Exception, qui est le type de base pour toutes les exceptions du .NET Framework. Toutefois, dans le code ci-dessus, je caste l’objet Exception retourné par GetLastError un HttpException objet. Si l’événement Error est déclenché, car une exception a été levée pendant le traitement d’une ressource ASP.NET, l’exception levée est encapsulée dans un HttpException. Pour obtenir l’exception réelle qui a précédé l’événement Error, utilisez la InnerException propriété. Si l’événement Error a été déclenché en raison d’une exception basée sur HTTP, telle qu’une requête pour une page inexistante, une HttpException exception est levée, mais elle n’a pas d’exception interne.

Le code suivant utilise les GetLastErrormessage informations permettant de récupérer des informations sur l’exception qui a déclenché l’événement Error , en stockant celui-ci HttpException dans une variable nommée lastErrorWrapper. Il stocke ensuite le type, le message et la trace de pile de l’exception d’origine dans trois variables de chaîne, en vérifiant si l’exception lastErrorWrapper est l’exception réelle qui a déclenché l’événement Error (dans le cas d’exceptions basées sur HTTP) ou s’il s’agit simplement d’un wrapper pour une exception levée lors du traitement de la requête.

protected void Application_Error(object sender, EventArgs e)
{
    // Get the error details
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;

    Exception lastError = lastErrorWrapper;
    if (lastErrorWrapper.InnerException != null)
        lastError = lastErrorWrapper.InnerException;

    string lastErrorTypeName = lastError.GetType().ToString();
    string lastErrorMessage = lastError.Message;
    string lastErrorStackTrace = lastError.StackTrace;
}

À ce stade, vous disposez de toutes les informations dont vous avez besoin pour écrire du code qui journalisera les détails de l’exception dans une table de base de données. Vous pouvez créer une table de base de données avec des colonnes pour chacun des détails d’erreur d’intérêt ( le type, le message, la trace de pile, etc.), ainsi que d’autres informations utiles, telles que l’URL de la page demandée et le nom de l’utilisateur actuellement connecté. Dans le Application_Error gestionnaire d’événements, vous devez ensuite vous connecter à la base de données et insérer un enregistrement dans la table. De même, vous pouvez ajouter du code pour alerter un développeur de l’erreur par e-mail.

Les bibliothèques de journalisation des erreurs examinées dans les deux tutoriels suivants fournissent ces fonctionnalités prêtes à l’emploi. Il n’est donc pas nécessaire de générer cette journalisation des erreurs et de vous-même de les signaler. Toutefois, pour illustrer que l’événement Error est déclenché et que le Application_Error gestionnaire d’événements peut être utilisé pour consigner les détails de l’erreur et avertir un développeur, nous allons ajouter du code qui avertit un développeur lorsqu’une erreur se produit.

Notification d’un développeur lorsqu’une exception non gérée se produit

Lorsqu’une exception non gérée se produit dans l’environnement de production, il est important d’alerter l’équipe de développement afin qu’elle puisse évaluer l’erreur et déterminer les actions à entreprendre. Par exemple, s’il existe une erreur lors de la connexion à la base de données, vous devez vérifier votre chaîne de connexion et, peut-être, ouvrir un ticket de support auprès de votre société d’hébergement web. Si l’exception s’est produite en raison d’une erreur de programmation, un code ou une logique de validation supplémentaire peut être ajouté pour empêcher ces erreurs à l’avenir.

Les classes .NET Framework dans l’espace System.Net.Mail de noms facilitent l’envoi d’un e-mail. La MailMessage classe représente un message électronique et a des propriétés telles que To, , From, Subject, Bodyet Attachments. Utilisé SmtpClass pour envoyer un MailMessage objet à l’aide d’un serveur SMTP spécifié ; les paramètres du serveur SMTP peuvent être spécifiés par programmation ou de manière déclarative dans l’élément <system.net> du .Web.config file Pour plus d’informations sur l’envoi de messages électroniques dans une application ASP.NET, consultez mon article, l’envoi de courrier électronique à partir d’un site pages Web ASP.NET et system.Net.Mail.

Remarque

L’élément <system.net> contient les paramètres du serveur SMTP utilisés par la classe lors de l’envoi SmtpClient d’un e-mail. Votre entreprise d’hébergement web dispose probablement d’un serveur SMTP que vous pouvez utiliser pour envoyer des e-mails à partir de votre application. Pour plus d’informations sur les paramètres du serveur SMTP, consultez la section de support de votre hôte web.

Ajoutez le code suivant au Application_Error gestionnaire d’événements pour envoyer un e-mail au développeur lorsqu’une erreur se produit :

void Application_Error(object sender, EventArgs e)
{
    // Get the error details
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;

    Exception lastError = lastErrorWrapper;
    if (lastErrorWrapper.InnerException != null)
        lastError = lastErrorWrapper.InnerException;

    string lastErrorTypeName = lastError.GetType().ToString();
    string lastErrorMessage = lastError.Message;
    string lastErrorStackTrace = lastError.StackTrace;

    const string ToAddress = "support@example.com";
    const string FromAddress = "support@example.com";
    const string Subject = "An Error Has Occurred!";
    
    // Create the MailMessage object
    MailMessage mm = new MailMessage(FromAddress, ToAddress);
    mm.Subject = Subject;
    mm.IsBodyHtml = true;
    mm.Priority = MailPriority.High;
    mm.Body = string.Format(@"
<html>
<body>
  <h1>An Error Has Occurred!</h1>
  <table cellpadding=""5"" cellspacing=""0"" border=""1"">
  <tr>
  <tdtext-align: right;font-weight: bold"">URL:</td>
  <td>{0}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">User:</td>
  <td>{1}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">Exception Type:</td>
  <td>{2}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">Message:</td>
  <td>{3}</td>
  </tr>
  <tr>
  <tdtext-align: right;font-weight: bold"">Stack Trace:</td>
  <td>{4}</td>
  </tr> 
  </table>
</body>
</html>",
        Request.RawUrl,
        User.Identity.Name,
        lastErrorTypeName,
        lastErrorMessage,
        lastErrorStackTrace.Replace(Environment.NewLine, "<br />"));

    // Attach the Yellow Screen of Death for this error   
    string YSODmarkup = lastErrorWrapper.GetHtmlErrorMessage();
    if (!string.IsNullOrEmpty(YSODmarkup))
    {
        Attachment YSOD = 
            Attachment.CreateAttachmentFromString(YSODmarkup, "YSOD.htm");
        mm.Attachments.Add(YSOD);
    }

    // Send the email
    SmtpClient smtp = new SmtpClient();
    smtp.Send(mm);
}

Bien que le code ci-dessus soit assez long, la majeure partie crée le code HTML qui apparaît dans l’e-mail envoyé au développeur. Le code commence par référencer le HttpException retourné par la GetLastError méthode (lastErrorWrapper). L’exception réelle qui a été déclenchée par la requête est récupérée via lastErrorWrapper.InnerException et est affectée à la variable lastError. Les informations de suivi de type, de message et de lastError pile sont extraites et stockées dans trois variables de chaîne.

Ensuite, un MailMessage objet nommé mm est créé. Le corps de l’e-mail est au format HTML et affiche l’URL de la page demandée, le nom de l’utilisateur actuellement connecté et les informations relatives à l’exception (le type, le message et la trace de pile). L’une des choses intéressantes sur la HttpException classe est que vous pouvez générer le code HTML utilisé pour créer l’écran jaune des détails de l’exception de mort (YSOD) en appelant la méthode GetHtmlErrorMessage. Cette méthode est utilisée ici pour récupérer le balisage YSOD Détails de l’exception et l’ajouter à l’e-mail en tant que pièce jointe. Un mot de prudence : si l’exception qui a déclenché l’événement Error était une exception basée sur HTTP (par exemple, une demande pour une page inexistante), la GetHtmlErrorMessage méthode retourne null.

La dernière étape consiste à envoyer le MailMessage. Pour ce faire, créez une méthode SmtpClient et appelez sa Send méthode.

Remarque

Avant d’utiliser ce code dans votre application web, vous souhaiterez modifier les valeurs dans les constantes et FromAddress les valeurs de support@example.com l’adresse ToAddress e-mail de l’adresse e-mail à laquelle l’e-mail de notification d’erreur doit être envoyé et provenir. Vous devez également spécifier les paramètres du serveur SMTP dans la <system.net> section dans Web.config. Consultez votre fournisseur d’hôtes web pour déterminer les paramètres du serveur SMTP à utiliser.

Avec ce code en place chaque fois qu’il y a une erreur, le développeur est envoyé un message électronique qui récapitule l’erreur et inclut YSOD. Dans le tutoriel précédent, nous avons démontré une erreur d’exécution en visitant Genre.aspx et en transmettant une valeur non valide ID via la chaîne de requête, par exemple Genre.aspx?ID=foo. La visite de la page avec le Global.asax fichier en place génère la même expérience utilisateur que dans le didacticiel précédent . Dans l’environnement de développement, vous continuerez à voir l’écran jaune des détails de l’exception jaune de la mort, tandis que dans l’environnement de production, vous verrez la page d’erreur personnalisée. En plus de ce comportement existant, le développeur est envoyé un e-mail.

La figure 2 montre l’e-mail reçu lors de la visite Genre.aspx?ID=foo. Le corps de l’e-mail récapitule les informations d’exception, tandis que la YSOD.htm pièce jointe affiche le contenu affiché dans le YSOD Détails de l’exception (voir la figure 3).

Capture d’écran montrant l’e-mail envoyé au développeur.

Figure 2 : Le développeur est envoyé une notification par e-mail chaque fois qu’il existe une exception non gérée
(Cliquez pour afficher l’image de taille complète)

Capture d’écran montrant que la notification par e-mail inclut les détails de l’exception Y SSO D en tant que pièce jointe.

Figure 3 : La notification par e-mail inclut les détails de l’exception YSOD en tant que pièce jointe
(Cliquez pour afficher l’image de taille complète)

Qu’en est-il de l’utilisation de la page d’erreur personnalisée ?

Ce tutoriel a montré comment utiliser Global.asax et le gestionnaire d’événements pour exécuter du Application_Error code lorsqu’une exception non gérée se produit. Plus précisément, nous avons utilisé ce gestionnaire d’événements pour informer un développeur d’une erreur ; nous pourrions également l’étendre pour enregistrer les détails de l’erreur dans une base de données. La présence du gestionnaire d’événements Application_Error n’affecte pas l’expérience de l’utilisateur final. Ils voient toujours la page d’erreur configurée, qu’il s’agit des détails de l’erreur YSOD, de l’erreur runtime YSOD ou de la page d’erreur personnalisée.

Il est naturel de se demander si le fichier et Application_Error l’événement sont nécessaires lors de l’utilisation Global.asax d’une page d’erreur personnalisée. Lorsqu’une erreur se produit, l’utilisateur affiche la page d’erreur personnalisée. Pourquoi ne pouvons-nous pas placer le code pour avertir le développeur et enregistrer les détails de l’erreur dans la classe code-behind de la page d’erreur personnalisée ? Bien que vous puissiez certainement ajouter du code à la classe code-behind de la page d’erreur personnalisée, vous n’avez pas accès aux détails de l’exception qui a déclenché l’événement Error lors de l’utilisation de la technique que nous avons explorée dans le didacticiel précédent. L’appel de la GetLastError méthode à partir de la page d’erreur personnalisée retourne Nothing.

La raison de ce comportement est que la page d’erreur personnalisée est atteinte via une redirection. Lorsqu’une exception non gérée atteint le ASP.NET runtime, le moteur ASP.NET déclenche son Error événement (qui exécute le Application_Error gestionnaire d’événements), puis redirige l’utilisateur vers la page d’erreur personnalisée en émettant un Response.Redirect(customErrorPageUrl). La Response.Redirect méthode envoie une réponse au client avec un code d’état HTTP 302, demandant au navigateur de demander une nouvelle URL, à savoir la page d’erreur personnalisée. Le navigateur demande ensuite automatiquement cette nouvelle page. Vous pouvez indiquer que la page d’erreur personnalisée a été demandée séparément de la page où l’erreur provient, car la barre d’adresses du navigateur passe à l’URL de la page d’erreur personnalisée (voir la figure 4).

Capture d’écran montrant que le navigateur est redirigé lorsqu’une erreur se produit.

Figure 4 : Lorsqu’une erreur se produit, le navigateur est redirigé vers l’URL de la page d’erreur personnalisée
(Cliquez pour afficher l’image de taille complète)

L’effet net est que la requête où l’exception non gérée s’est produite se termine lorsque le serveur répond avec la redirection HTTP 302. La demande suivante à la page d’erreur personnalisée est une nouvelle demande ; à ce stade, le moteur de ASP.NET a ignoré les informations d’erreur et, par ailleurs, n’a aucun moyen d’associer l’exception non gérée dans la demande précédente à la nouvelle demande pour la page d’erreur personnalisée. C’est pourquoi retourne null une fois GetLastError appelé à partir de la page d’erreur personnalisée.

Toutefois, il est possible que la page d’erreur personnalisée s’exécute pendant la même requête qui a provoqué l’erreur. La Server.Transfer(url) méthode transfère l’exécution à l’URL spécifiée et la traite dans la même requête. Vous pouvez déplacer le code dans le Application_Error gestionnaire d’événements vers la classe code-behind de la page d’erreur personnalisée, en la remplaçant par Global.asax le code suivant :

protected void Application_Error(object sender, EventArgs e)
{
    // Transfer the user to the appropriate custom error page
    HttpException lastErrorWrapper = 
        Server.GetLastError() as HttpException;

    if (lastErrorWrapper.GetHttpCode() == 404)
    {
        Server.Transfer("~/ErrorPages/404.aspx");
    }
    else
    {
        Server.Transfer("~/ErrorPages/Oops.aspx");
    }
}

À présent, lorsqu’une exception non gérée se produit, le gestionnaire d’événements transfère le Application_Error contrôle à la page d’erreur personnalisée appropriée en fonction du code d’état HTTP. Étant donné que le contrôle a été transféré, la page d’erreurs personnalisée a accès aux informations d’exception non gérées via Server.GetLastError et peut avertir un développeur de l’erreur et consigner ses détails. L’appel Server.Transfer empêche le moteur ASP.NET de rediriger l’utilisateur vers la page d’erreur personnalisée. Au lieu de cela, le contenu de la page d’erreur personnalisée est retourné en tant que réponse à la page qui a généré l’erreur.

Résumé

Lorsqu’une exception non gérée se produit dans une application web ASP.NET, le runtime ASP.NET déclenche l’événement Error et affiche la page d’erreur configurée. Nous pouvons informer le développeur de l’erreur, consigner ses détails ou le traiter d’une autre manière, en créant un gestionnaire d’événements pour l’événement Error. Il existe deux façons de créer un gestionnaire d’événements pour HttpApplication les événements tels que Error: dans le Global.asax fichier ou à partir d’un module HTTP. Ce tutoriel a montré comment créer un gestionnaire d’événements Error dans le Global.asax fichier qui avertit les développeurs d’une erreur au moyen d’un message électronique.

La création d’un gestionnaire d’événements Error est utile si vous devez traiter des exceptions non gérées de manière unique ou personnalisée. Toutefois, la création de votre propre Error gestionnaire d’événements pour enregistrer l’exception ou informer un développeur n’est pas l’utilisation la plus efficace de votre temps, car il existe déjà des bibliothèques de journalisation des erreurs gratuites et faciles à utiliser qui peuvent être configurées en quelques minutes. Les deux didacticiels suivants examinent deux bibliothèques de ce type.

Bonne programmation !

Pour aller plus loin

Pour plus d’informations sur les sujets abordés dans ce tutoriel, consultez les ressources suivantes :