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 d’avertir un développeur et de consigner l’erreur afin qu’elle puisse être diagnostiquée ultérieurement. 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 prise en charge s’affiche dans le ASP.NET runtime.

Introduction

Lorsqu’une exception non prise en charge se produit dans une application ASP.NET, elle s’affiche jusqu’à l’ASP.NET runtime, ce qui déclenche l’événement Error et affiche la page d’erreur appropriée. Il existe trois types de pages d’erreur : l’écran jaune de l’erreur d’exécution de la mort (YSOD) ; les détails de l’exception YSOD ; et pages d’erreur 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’erreur personnalisée qui correspond à l’apparence du site est préférable à l’erreur d’exécution par défaut YSOD, mais l’affichage d’une page d’erreur personnalisée n’est qu’une partie d’une solution complète de gestion des erreurs. 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écouvrir la cause de l’exception et y remédier. En outre, les détails de l’erreur doivent être consignés afin que l’erreur puisse être examinée et diagnostiquée ultérieurement.

Ce tutoriel montre comment accéder aux détails d’une exception non gérée afin qu’elles puissent être journalisées et qu’un développeur soit averti. Les deux tutoriels suivants explorent les bibliothèques de journalisation des erreurs qui, après un peu de configuration, informent automatiquement les développeurs des erreurs d’exécution et consignent leurs détails.

Notes

Les informations examinées dans ce didacticiel sont les plus utiles si vous devez traiter des exceptions non gérées d’une manière unique ou personnalisée. Dans les cas où il vous suffit de consigner l’exception et d’avertir un développeur, l’utilisation d’une bibliothèque de journalisation des erreurs est la solution. 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 qu’un événement intéressant s’est produit et 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 y placez votre code. Étant donné que le runtime ASP.NET déclenche son Error événement chaque fois qu’une exception non prise en charge se produit, il s’ensuit que le code pour la journalisation des détails de l’erreur va 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 permettent au développeur de pages d’exécuter une logique personnalisée aux différents moments 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 portant le nom Global.asax.

Sceenshot qui met en évidence le fichier point global A S A X.

Figure 1 : Ajouter Global.asax à votre application web
(Cliquez pour afficher l’image en taille réelle)

Le contenu et la Global.asax structure du 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, le Global.asax est implémenté sous la forme de deux fichiers distincts : Global.asax et Global.asax.cs. Le Global.asax fichier ne contient qu’une @Application directive qui fait référence au .cs fichier ; les gestionnaires d’événements d’intérêt sont définis dans le Global.asax.cs fichier. Pour les WSP, un seul fichier est créé, Global.asaxet les gestionnaires d’événements sont définis dans un <script runat="server"> bloc.

Le Global.asax fichier créé dans un modèle wap 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, Session_Start, Application_Endet Session_End, qui sont des gestionnaires d’événements qui se déclenchent au démarrage de l’application web, au démarrage d’une nouvelle session, à la fin de l’application et à la fin d’une session, respectivement. Le Global.asax fichier créé dans un WSP par Visual Studio contient uniquement les Application_Errorgestionnaires d’événements , Application_Start, Session_Start, Application_Endet Session_End .

Notes

Lorsque vous déployez l’application ASP.NET, vous devez copier le fichier dans Global.asax l’environnement de production. Le Global.asax.cs fichier, qui est 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 Application_EventNamed’événements . 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 tutoriel, 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.

Notes

Les modules HTTP offrent une autre façon de définir des gestionnaires d’événements pour HttpApplication les événements. Les modules HTTP sont créés sous la forme d’un fichier de classe qui peut être placé directement dans le projet d’application web ou séparé 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 la création HttpApplication de 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 de supprimer l’assembly dans le Bin dossier et d’inscrire le module dans Web.config. Ce didacticiel ne traite pas de la création et de 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, consultez Utilisation de modules et de gestionnaires HTTP pour créer des composants de 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 qui a été levée. Utilisez la méthode de GetLastError l’objet 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 dans le .NET Framework. Toutefois, dans le code ci-dessus, je transforme l’objet Exception retourné par GetLastError dans un HttpException objet. Si l’événement Error est déclenché parce qu’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écipité 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 demande pour une page inexistante, un HttpException est levé, mais il n’a pas d’exception interne.

Le code suivant utilise pour récupérer des GetLastErrormessage informations sur l’exception qui a déclenché l’événement Error , en stockant le 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 lastErrorWrapper est l’exception réelle qui a déclenché l’événement Error (dans le cas des exceptions basées sur HTTP) ou s’il s’agit simplement d’un wrapper pour une exception levée lors du traitement de la demande.

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 qui vous intéressent (type, message, 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 vous connectez ensuite à la base de données et insérez 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 didacticiels suivants fournissent ces fonctionnalités prêtes à l’emploi. Il n’est donc pas nécessaire de générer cette journalisation et cette notification des erreurs vous-même. 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 notifier 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 prise en charge se produit

Lorsqu’une exception non prise en charge 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, en cas d’erreur lors de la connexion à la base de données, vous devrez doubler case activée 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, il peut être nécessaire d’ajouter du code supplémentaire ou une logique de validation pour empêcher de telles erreurs à l’avenir.

Les classes .NET Framework de l’espace de noms facilitent l’envoiSystem.Net.Mail d’un e-mail. La MailMessage classe représente un message électronique et possède des propriétés telles que To, From, Subject, Bodyet Attachments. SmtpClass est utilisé 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 programme ou de manière déclarative dans l’élément<system.net> dans .Web.config file Pour plus d’informations sur l’envoi de messages électroniques dans une application ASP.NET, case activée mon article Envoi de Email à partir d’un site pages Web ASP.NET et le FAQ System.Net.Mail.

Notes

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

Ajoutez le code suivant au gestionnaire d’événements Application_Error pour envoyer un e-mail à un 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 de celui-ci 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é levée par la requête est récupérée via lastErrorWrapper.InnerException et est affectée à la variable lastError. Les informations de trace de type, de message et de pile sont récupérées à partir de lastError 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 des informations sur l’exception (le type, le message et la trace de pile). L’une des choses intéressantes de la HttpException classe est que vous pouvez générer le code HTML utilisé pour créer l’écran jaune des détails d’exception de la 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 mise en garde : 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.

Notes

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

Avec ce code en place chaque fois qu’une erreur se produit, le développeur reçoit un message électronique récapitulant l’erreur et incluant le 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, comme 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 tutoriel précédent. Dans l’environnement de développement, vous continuez à voir l’écran jaune des détails de l’exception de la mort, tandis que dans l’environnement de production, vous verrez la page d’erreur personnalisée. En plus de ce comportement existant, un e-mail est envoyé au développeur.

La figure 2 montre l’e-mail reçu lors de la visite de 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 figure 3).

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

Figure 2 : Le développeur reçoit une notification Email chaque fois qu’il existe une exception non prise en charge
(Cliquez pour afficher l’image en taille réelle)

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

Figure 3 : La notification Email inclut les détails de l’exception YSOD en tant que pièce jointe
(Cliquez pour afficher l’image en taille réelle)

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 prise en charge se produit. Plus précisément, nous avons utilisé ce gestionnaire d’événements pour informer un développeur d’une erreur ; nous pouvons l’étendre pour consigner également les détails de l’erreur dans une base de données. La présence du Application_Error gestionnaire d’événements 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 d’exécution YSOD ou de la page d’erreur personnalisée.

Il est naturel de se demander si le fichier et Application_Error l’événement Global.asax sont nécessaires lors de l’utilisation d’une page d’erreur personnalisée. Lorsqu’une erreur se produit, la page d’erreur personnalisée s’affiche à l’utilisateur. 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 lors de l’utilisation Error de la technique que nous avons explorée dans le tutoriel 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 HTTP 302 status, en demandant au navigateur de demander une nouvelle URL, à savoir la page d’erreur personnalisée. Le navigateur demande alors automatiquement cette nouvelle page. Vous pouvez indiquer que la page d’erreur personnalisée a été demandée séparément de la page d’origine de l’erreur, car la barre d’adresse du navigateur change en URL de la page d’erreur personnalisée (voir 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 en taille réelle)

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

Toutefois, il est possible que la page d’erreur personnalisée soit exécutée lors de la même demande 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 du Application_Error gestionnaire d’événements vers la classe code-behind de la page d’erreur personnalisée, en le 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 Application_Error gestionnaire d’événements transfère le contrôle vers la page d’erreur personnalisée appropriée en fonction du code http status. Étant donné que le contrôle a été transféré, la page d’erreur personnalisée a accès aux informations d’exception non gérées via Server.GetLastError et peut informer un développeur de l’erreur et enregistrer 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 la 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 des événements comme 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 d’une manière unique ou personnalisée. Toutefois, la création de votre propre Error gestionnaire d’événements pour journaliser l’exception ou avertir 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 !

En savoir plus

Pour plus d’informations sur les sujets abordés dans ce tutoriel, reportez-vous aux ressources suivantes :