128 votes

L'erreur d'application ne se déclenche pas lorsque customerrors = "On".

J'ai du code dans le global.asax du fichier Application_Error qui s'exécute lorsqu'une erreur se produit et m'envoie par courrier électronique les détails de l'erreur.

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();

    if (error.Message != "Not Found")
    {
        // Send email here...
    }

}

Cela fonctionne bien lorsque je l'exécute dans Visual Studio, mais lorsque je le publie sur notre serveur en direct, l'écran de l'utilisateur se met à clignoter. Application_Error ne se déclenche pas.

Après quelques tests, je peux obtenir le Application_Error tirant quand je mets customErrors="Off" mais en le remettant à customErrors="On" empêche l'événement de se déclencher à nouveau.

Quelqu'un peut-il suggérer pourquoi Application_Error ne se déclencherait pas lorsque customErrors sont activés dans le web.config ?

0 votes

J'ai exactement le même problème. J'ai également trouvé cette question SO : stackoverflow.com/questions/3713939/ qui suggère de mettre le serveur IIS7 en mode classique. Malheureusement, ce n'est pas une option pour nous. Quelqu'un a-t-il une meilleure solution ?

0 votes

Voici une autre question connexe et ses réponses (dont aucune n'est acceptée) suggèrent de ne pas utiliser du tout Application_Error()... stackoverflow.com/questions/1194578/

0 votes

@Gweebz J'ai posté une réponse sur la façon dont j'ai contourné ce problème, mais je n'ai toujours pas trouvé de documentation solide sur la raison de ce comportement.

138voto

Jesse Webb Points 8015

UPDATE
Puisque cette réponse fournit une solution, je ne la modifierai pas, mais j'ai trouvé une façon beaucoup plus propre de résoudre ce problème. Voir mon autre réponse pour les détails...

Réponse originale :
J'ai compris pourquoi le Application_Error() n'est pas invoquée...

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // this line is the culprit
    }
...
}

Par défaut (lorsqu'un nouveau projet est généré), une application MVC possède une certaine logique dans la section Global.asax.cs fichier. Cette logique est utilisée pour le mappage des routes et l'enregistrement des filtres. Par défaut, il n'enregistre qu'un seul filtre : un HandleErrorAttribute filtre. Lorsque les customErrors sont activés (ou par le biais de requêtes distantes lorsqu'ils sont définis sur RemoteOnly), l'attribut HandleErrorAttribute indique à MVC de rechercher une vue Error et il n'appelle jamais la fonction Application_Error() méthode. Je n'ai pas pu trouver de documentation à ce sujet mais cela est expliqué dans cette réponse sur programmers.stackexchange.com .

Pour obtenir la méthode ApplicationError() appelée pour chaque exception non gérée, il suffit de supprimer la ligne qui enregistre le filtre HandleErrorAttribute.

Maintenant le problème est : Comment configurer les customErrors pour obtenir ce que vous voulez...

La section customErrors a la valeur par défaut suivante redirectMode="ResponseRedirect" . Vous pouvez également spécifier que l'attribut defaultRedirect est une route MVC. J'ai créé un ErrorController qui était très simple et j'ai modifié mon web.config pour qu'il ressemble à ceci...

web.config

<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
  <error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>

Le problème avec cette solution est qu'elle effectue une redirection 302 vers vos URL d'erreur et que ces pages répondent avec un code d'état 200. Cela conduit Google à indexer les pages d'erreur, ce qui est mauvais. Elle n'est pas non plus très conforme aux spécifications HTTP. Ce que je voulais faire, c'était ne pas rediriger et remplacer la réponse originale par mes vues d'erreur personnalisées.

J'ai essayé de changer redirectMode="ResponseRewrite" . Malheureusement, cette option ne prend pas en charge les routes MVC uniquement des pages HTML statiques ou ASPX. J'ai d'abord essayé d'utiliser une page HTML statique mais le code de réponse était toujours 200 mais, au moins, il n'y avait pas de redirection. J'ai alors eu une idée de cette réponse ...

J'ai décidé d'abandonner le MVC pour la gestion des erreurs. J'ai créé un Error.aspx et un PageNotFound.aspx . Ces pages étaient très simples mais elles avaient une pièce magique...

<script type="text/C#" runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
    }
</script>

Ce bloc indique à la page d'être servie avec le code d'état correct. Bien sûr, sur la page PageNotFound.aspx, j'ai utilisé HttpStatusCode.NotFound à la place. J'ai modifié mon web.config pour qu'il ressemble à ceci...

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>

Tout a fonctionné à merveille !

Résumé :

  • Retirez la ligne : filters.Add(new HandleErrorAttribute());
  • Utilice Application_Error() pour enregistrer les exceptions
  • Utiliser customErrors avec un ResponseRewrite, en pointant sur les pages ASPX
  • Rendez les pages ASPX responsables de leurs propres codes d'état de réponse.

Il y a quelques inconvénients que j'ai remarqués avec cette solution.

  • Les pages ASPX ne pouvant partager aucune balise avec les modèles Razor, j'ai dû réécrire les balises standard d'en-tête et de pied de page de notre site Web pour obtenir un aspect et une convivialité cohérents.
  • Les pages *.aspx sont accessibles directement en cliquant sur leur URL.

Il existe des solutions de contournement pour ces problèmes, mais je n'étais pas assez préoccupé par ceux-ci pour faire un travail supplémentaire.

J'espère que cela aidera tout le monde !

2 votes

+1 ! Très bonne solution de contournement mais, quelles pourraient être les conséquences de mélanger MVC avec des pages aspx ?

2 votes

Nous l'avons installé dans PROD depuis quelques mois maintenant et je n'ai pas trouvé d'impact négatif. Le seul problème est que nous avons dû modifier notre CI et notre déploiement pour effectuer une tâche AspCompile MSBUILD, car MVC n'en a pas besoin. .comme les dossiers, ils l'ont exigé. Il y a peut-être d'autres problèmes, mais ils ne sont pas encore apparus...

0 votes

J'ai essayé d'utiliser cette approche dans MVC4, et apparemment cela ne fonctionne pour moi que lorsque j'obtiens <customErrors mode="Off" /> . Ayant filters.Add(new HandleErrorAttribute()); retiré ou non n'a aucun effet.

77voto

Mark Points 481

J'ai résolu ce problème en créant un ExceptionFilter et en y enregistrant l'erreur au lieu de Application_Error. Tout ce que vous avez à faire est d'ajouter un appel à in dans RegisterGlobalFilters

log4netExceptionFilter.cs

using System
using System.Web.Mvc;

public class log4netExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        if (!(ex is HttpException)) //ignore "file not found"
        {
            //Log error here
        }
    }
}

Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());
}

6 votes

+1 Si cette réponse fonctionne, il me semble que c'est la manière la plus élégante et peut-être voulue (par le cadre MVC) de gérer les erreurs.

2 votes

Cela fonctionne parfaitement pour moi et est beaucoup plus propre que la réponse acceptée ici. Joli !

3 votes

Cela permettra de résoudre le problème de l'enregistrement des exceptions. Comment avez-vous combiné cette solution avec l'affichage de pages d'erreur personnalisées ?

37voto

Jesse Webb Points 8015

J'ai trouvé un article qui décrit une façon beaucoup plus propre de créer des pages d'erreur personnalisées dans une application Web MVC3, sans empêcher la possibilité d'enregistrer les exceptions.

La solution consiste à utiliser le <httpErrors> de l'élément <system.webServer> section.

J'ai configuré mon Web.config comme ça...

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

J'ai également configuré customErrors d'avoir mode="Off" (comme le suggère l'article).

Ainsi, les réponses sont remplacées par les actions d'un ErrorController. Voici ce contrôleur :

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
}

Les vues sont très simples, j'ai juste utilisé la syntaxe standard de Razor pour créer les pages.

Ce seul élément devrait vous suffire pour utiliser des pages d'erreur personnalisées avec MVC.

J'avais également besoin d'un enregistrement des exceptions, j'ai donc volé le fichier La solution de Mark d'utiliser un ExceptionFilter personnalisé...

public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    }
}

La dernière chose que vous devez faire est d'enregistrer le filtre d'exception dans votre système de gestion de l'information. Global.asax.cs fichier :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());
}

Cette solution me semble beaucoup plus propre que ma réponse précédente et fonctionne tout aussi bien, pour autant que je puisse en juger. Je l'aime particulièrement parce que je n'ai pas eu l'impression de me battre contre le framework MVC ; cette solution en tire parti !

6 votes

Il convient de mentionner que cette solution ne fonctionne qu'avec IIS 7 et les versions plus récentes ; l'élément httpErrors n'a été ajouté que récemment.

0 votes

Cette réponse n'a pas fonctionné pour moi, elle m'a, elle m'a joué un horrible bleu : HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. Écran d'erreur, pas ma demande /Error/Index page

0 votes

@SerjSagan Je viens d'essayer cela dans un nouveau projet pour tester les étapes et cela fonctionne bien. Utilisez-vous IIS, IIS Express ou VS Dev Server ? Cela ne fonctionnera pas avec VS Dev Server. Lorsque j'ai configuré mon projet pour utiliser VS Dev Server, et non IIS, j'ai remarqué des erreurs Yellow-screen au lieu de pages d'erreur personnalisées.

8voto

Clark Kent Points 5845

En cas de ASP.NET MVC5 utilice

public class ExceptionPublisherExceptionFilter : IExceptionFilter
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        public void OnException(ExceptionContext exceptionContext)
        {
            var exception = exceptionContext.Exception;
            var request = exceptionContext.HttpContext.Request;
            // HttpException httpException = exception as HttpException;
            // Log this exception with your logger
            _logger.Error(exception.Message);
        }
    }

Vous pouvez le trouver dans FilterConfig.cs de App_Start dossier.

1voto

Tim Hardy Points 261

J'aime la réponse de Mark avec l'ExceptionFilter, mais une autre option, si tous vos contrôleurs dérivent du même contrôleur de base, est de simplement surcharger OnException dans votre contrôleur de base. Vous pouvez y effectuer la journalisation et l'envoi d'e-mails. Cela présente l'avantage de pouvoir utiliser toutes les dépendances que vous avez déjà injectées dans votre contrôleur de base avec votre conteneur IoC.

Vous pouvez toujours utiliser votre IoC avec un IExceptionFilter, mais la configuration de vos liaisons est un peu plus délicate.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X