Application_Error wird nicht ausgelöst, wenn customerrors = "On"

124

Ich habe Code im Ereignis der global.asaxDatei, Application_Errorder ausgeführt wird, wenn ein Fehler auftritt, und Details des Fehlers per E-Mail an mich selbst sendet.

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

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

}

Dies funktioniert einwandfrei, wenn ich es in Visual Studio ausführe. Wenn ich es jedoch auf unserem Live-Server veröffentliche, wird das Application_ErrorEreignis nicht ausgelöst.

Nach einigen Tests kann ich das Application_ErrorAuslösen erhalten, wenn ich es einstelle. Wenn ich es customErrors="Off"jedoch zurücksetze, wird customErrors="On"das Ereignis nicht mehr ausgelöst.

Kann jemand vorschlagen, warum Application_Errornicht feuern würde, wenn customErrorsin der aktiviert sind web.config?

WDuffy
quelle
Ich habe genau das gleiche Problem. Ich habe auch diese SO-Frage gefunden: stackoverflow.com/questions/3713939/…, die vorschlägt, den IIS7-Server in den klassischen Modus zu versetzen. Leider ist das für uns keine Option. Hat jemand bessere Lösungen?
Jesse Webb
Hier ist eine andere verwandte Frage und ihre Antworten (von denen keine akzeptiert wird) schlagen vor, Application_Error () überhaupt nicht zu verwenden ... stackoverflow.com/questions/1194578/…
Jesse Webb
@Gweebz Ich habe eine Antwort darauf gepostet, wie ich damit umgegangen bin, aber ich habe immer noch keine solide Dokumentation gefunden, warum ich dieses Verhalten bekommen habe.
WDuffy
Ich habe eine Antwort hinzugefügt, die erklärt, warum die Application_Error()Methode nicht aufgerufen wurde. Ich erklärte auch meine endgültige Lösung.
Jesse Webb
Empfehlen Sie diesen Artikel wirklich , damit benutzerdefinierte Fehler funktionieren.
ThomasArdal

Antworten:

133

UPDATE
Da diese Antwort eine Lösung bietet, werde ich sie nicht bearbeiten, aber ich habe einen viel saubereren Weg gefunden, um dieses Problem zu lösen. Siehe meine andere Antwort für Details ...

Ursprüngliche Antwort:
Ich habe herausgefunden, warum die Application_Error()Methode nicht aufgerufen wird ...

Global.asax.cs

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

Standardmäßig (wenn ein neues Projekt generiert wird) enthält eine MVC-Anwendung eine Logik in der Global.asax.csDatei. Diese Logik wird zum Zuordnen von Routen und zum Registrieren von Filtern verwendet. Standardmäßig wird nur ein Filter registriert: ein HandleErrorAttributeFilter. Wenn customErrors aktiviert ist (oder über Remote-Anforderungen, wenn RemoteOnly festgelegt ist), weist das HandleErrorAttribute MVC an, nach einer Fehleransicht zu suchen, und ruft die Application_Error()Methode niemals auf . Ich konnte keine Dokumentation dazu finden, aber diese wird in dieser Antwort auf programmers.stackexchange.com erläutert .

Um die ApplicationError () -Methode für jede nicht behandelte Ausnahme aufzurufen, entfernen Sie einfach die Zeile, in der der HandleErrorAttribute-Filter registriert ist.

Das Problem ist nun: So konfigurieren Sie die customErrors, um das zu erhalten, was Sie möchten ...

Der Abschnitt customErrors ist standardmäßig redirectMode="ResponseRedirect". Sie können das Attribut defaultRedirect auch als MVC-Route angeben. Ich habe einen ErrorController erstellt, der sehr einfach war, und meine web.config so geändert, dass sie so aussieht ...

web.config

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

Das Problem bei dieser Lösung besteht darin, dass eine 302-Umleitung zu Ihren Fehler-URLs durchgeführt wird und diese Seiten dann mit einem 200-Statuscode antworten. Dies führt dazu, dass Google die Fehlerseiten indiziert, was schlecht ist. Es ist auch nicht sehr konform mit der HTTP-Spezifikation. Ich wollte nicht umleiten und die ursprüngliche Antwort mit meinen benutzerdefinierten Fehleransichten überschreiben.

Ich habe versucht mich zu ändern redirectMode="ResponseRewrite". Leider unterstützt diese Option keine MVC-Routen , nur statische HTML-Seiten oder ASPX. Ich habe zuerst versucht, eine statische HTML-Seite zu verwenden, aber der Antwortcode war immer noch 200, aber zumindest wurde er nicht umgeleitet. Ich habe dann eine Idee von dieser Antwort ...

Ich habe mich entschlossen, MVC wegen Fehlerbehandlung aufzugeben. Ich habe ein Error.aspxund ein erstellt PageNotFound.aspx. Diese Seiten waren sehr einfach, aber sie hatten ein Stück Magie ...

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

Dieser Block weist die Seite an, mit dem richtigen Statuscode bedient zu werden. Grob gesagt habe ich HttpStatusCode.NotFoundstattdessen auf der Seite PageNotFound.aspx verwendet . Ich habe meine web.config so geändert, dass sie so aussieht ...

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

Es hat alles perfekt funktioniert!

Zusammenfassung:

  • Entfernen Sie die Linie: filters.Add(new HandleErrorAttribute());
  • Verwenden Sie die Application_Error()Methode, um Ausnahmen zu protokollieren
  • Verwenden Sie customErrors mit einem ResponseRewrite, der auf ASPX-Seiten verweist
  • Machen Sie die ASPX-Seiten für ihre eigenen Antwortstatuscodes verantwortlich

Es gibt ein paar Nachteile, die ich bei dieser Lösung bemerkt habe.

  • Die ASPX-Seiten können kein Markup mit Razor-Vorlagen teilen. Ich musste das Standard-Markup für Kopf- und Fußzeilen unserer Website neu schreiben, um ein einheitliches Erscheinungsbild zu erzielen.
  • Auf die * .aspx-Seiten kann direkt zugegriffen werden, indem ihre URLs eingegeben werden

Es gibt Workarounds für diese Probleme, aber ich war nicht besorgt genug, um zusätzliche Arbeit zu leisten.

Ich hoffe das hilft allen!

Jesse Webb
quelle
2
+1! Wirklich gute Problemumgehung, aber was könnten die Konsequenzen des Mischens von MVC mit Aspx-Seiten sein?
Diego
2
Wir haben dies seit ein paar Monaten in PROD und ich habe keine negativen Auswirkungen gefunden. Das einzige Problem war, dass wir unser CI ändern und bereitstellen mussten, um eine AspCompile MSBUILD-Aufgabe auszuführen, da MVC es nicht benötigt, aber als wir die AS- Dateien hinzufügten , benötigten sie es. Es kann andere Probleme geben, aber sie sind noch nicht aufgetaucht ...
Jesse Webb
Ich habe versucht, diesen Ansatz in MVC4 zu verwenden, und anscheinend funktioniert er nur bei mir, wenn ich ihn habe <customErrors mode="Off" />. Nachdem filters.Add(new HandleErrorAttribute());entfernt oder nicht , keine Wirkung.
Grzegorz Sławecki
Auf der Aspx-Seite können Sie einen Ajax-Aufruf hinzufügen, um die Ansicht aufzurufen, die Sie anzeigen möchten. Spart das Kopieren von Code
Mike
71

Ich habe dieses Problem gelöst, indem ich einen ExceptionFilter erstellt und den Fehler dort anstelle von Application_Error protokolliert habe. Sie müssen lediglich einen Aufruf in in RegisterGlobalFilters hinzufügen

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());
}
Kennzeichen
quelle
6
+1 Wenn diese Antwort funktioniert, scheint sie mir die eleganteste und vielleicht beabsichtigte Methode (nach dem MVC-Framework) zu sein, um mit Fehlern umzugehen.
Matt Hamsmith
2
Das funktioniert perfekt für mich und ist viel ordentlicher als die hier akzeptierte Antwort. Nett!
Alex Warren
3
Dies wird zur Lösung des Problems der Protokollierung von Ausnahmen verwendet. Wie haben Sie dies mit der Anzeige benutzerdefinierter Fehlerseiten kombiniert?
Jesse Webb
3
Ich habe es versucht und es hat gut funktioniert, bis auf den Fall der 404er. Für 404s würde die Ansicht Errors.cshtml nicht angezeigt, sondern nur eine YSoD. Wenn keine benutzerdefinierten 404 erforderlich sind, ist diese Lösung definitiv sauberer!
Jesse Webb
1
Ich mag das. Funktioniert mit 'CustomErrors = On'
Illidan
36

Ich habe einen Artikel gefunden, der eine viel sauberere Methode zum Erstellen benutzerdefinierter Fehlerseiten in einer MVC3-Webanwendung beschreibt, die die Protokollierung der Ausnahmen nicht verhindert.

Die Lösung besteht darin, das <httpErrors>Element des <system.webServer>Abschnitts zu verwenden.

Ich habe meine Web.config so konfiguriert ...

<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>

Ich baute auch customErrorszu haben mode="Off"(wie durch den Artikel vorgeschlagen).

Dadurch werden die Antworten durch die Aktionen eines ErrorControllers überschrieben. Hier ist dieser Controller:

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

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

Die Ansichten sind sehr einfach. Ich habe nur die Standard-Razor-Syntax verwendet, um die Seiten zu erstellen.

Das allein sollte ausreichen, um benutzerdefinierte Fehlerseiten mit MVC zu verwenden.

Ich musste auch Ausnahmen protokollieren, damit ich die Lösung von Mark für die Verwendung eines benutzerdefinierten ExceptionFilter gestohlen habe ...

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

Als letztes müssen Sie den Ausnahmefilter in Ihrer Datei Global.asax.cs registrieren :

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

Dies scheint eine viel sauberere Lösung zu sein als meine vorherige Antwort und funktioniert, soweit ich das beurteilen kann, genauso gut. Ich mag es besonders, weil ich nicht das Gefühl hatte, gegen das MVC-Framework zu kämpfen. Diese Lösung nutzt es tatsächlich!

Jesse Webb
quelle
6
Es ist erwähnenswert, dass ich denke, dass diese Lösung nur in IIS 7 und neuer funktioniert. Das httpErrors-Element wurde erst kürzlich hinzugefügt.
Jesse Webb
Diese Antwort hat bei mir nicht funktioniert, sie hat mich, sie spielt mir ein schreckliches Blau: HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. Fehlerbildschirm, nicht meine angeforderte /Error/IndexSeite
Serj Sagan
@SerjSagan Ich habe dies gerade in einem neuen Projekt ausprobiert, um die Schritte zu testen, und es funktioniert gut. Verwenden Sie IIS, IIS Express oder VS Dev Server? Dies funktioniert in VS Dev Server nicht. Als ich mein Projekt auf VS Dev Server und nicht auf IIS eingestellt hatte, bemerkte ich Gelbbildfehler anstelle von benutzerdefinierten Fehlerseiten.
Jesse Webb
1
@SerjSagan Aber es hört sich so an, als würden IIS-Bluescreen-Fehler angezeigt, im Gegensatz zu den klassischen Yellow-Screen-Fehlern. Dies lässt mich annehmen, dass Sie irgendeine Form von IIS verwenden. Wenn ja, lesen Sie den Artikel durch, auf den ich im ersten Satz verwiesen habe, insbesondere den letzten Abschnitt mit dem Titel: "Wenige wichtige Hinweise". Es heißt:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
Jesse Webb
3
@SerjSagan Wenn Sie den errorMode in DetailedLocalOnly oder Custom ändern, wird Ihre Seite angezeigt.
Daniel P
8

Im Falle von ASP.NET MVC5 verwenden

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);
        }
    }

Sie können es in finden FilterConfig.csvon App_StartOrdnern.

Entwickler
quelle
1

Ich mag Marks Antwort mit dem ExceptionFilter, aber eine andere Option, wenn alle Ihre Controller von demselben Basiscontroller stammen, besteht darin, OnException in Ihrem Basiscontroller einfach zu überschreiben. Dort können Sie sich anmelden und E-Mails senden. Dies hat den Vorteil, dass Sie alle Abhängigkeiten verwenden können, die Sie bereits mit Ihrem IoC-Container in Ihren Basis-Controller eingefügt haben.

Sie können Ihr IoC weiterhin mit einem IExceptionFilter verwenden, die Konfiguration Ihrer Bindungen ist jedoch etwas schwieriger.

Tim Hardy
quelle
0

Soweit ich weiß, übergeben Sie die Kontrolle an die im URL-Parameter angegebene Seite, und Ihre Ereignisbenachrichtigung wird hier und nicht in Application_Error angezeigt

<customErrors defaultRedirect="myErrorPage.aspx"
              mode="On">
</customErrors>

Viele Informationen finden Sie hier: http://support.microsoft.com/kb/306355

ChrisBint
quelle
Vielen Dank, Chris. Ich habe die Dokumentation gelesen und es wird vorgeschlagen, dass Application_Error aufgerufen wird, wenn ein nicht behandelter Fehler auftritt (was ich erwarte). Sollte Server.ClearError () nicht aufgerufen werden, ist der Abschnitt web.config customErrors der letzte Handle-Punkt. Durch das Aktivieren von customErrors wird jedoch verhindert, dass Application_Error ausgelöst wird.
WDuffy
In der von Ihnen verlinkten Dokumentation geht es um ASP.NET-Anwendungen, und MVC3-Webanwendungen verhalten sich anscheinend anders.
Jesse Webb
0

Um dies zu umgehen, habe ich die benutzerdefinierten Fehler deaktiviert und alle Fehler aus dem Application_Error-Ereignis in global.asax behandelt. Mit MVC ist es etwas schwierig, da ich keine 301-Umleitung zurückgeben wollte, sondern geeignete Fehlercodes zurückgeben wollte. Weitere Details finden Sie in meinem Blog unter http://www.wduffy.co.uk/blog/using-application_error-in-asp-net-mvcs-global-asax-to-handle-errors/, aber der endgültige Code lautet nachfolgend aufgeführten...

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();
    var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

    if (code != 404)
    {
            // Generate email with error details and send to administrator
    }

    Response.Clear();
    Server.ClearError();

    string path = Request.Path;
    Context.RewritePath(string.Format("~/Errors/Http{0}", code), false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(Context);
    Context.RewritePath(path, false);
}

Und hier ist der Controller

public class ErrorsController : Controller
{

    [HttpGet]
    public ActionResult Http404(string source)
    {
            Response.StatusCode = 404;
            return View();
    }

    [HttpGet]
    public ActionResult Http500(string source)
    {
            Response.StatusCode = 500;
            return View();
    }

}
WDuffy
quelle
Ich habe Ihre Lösung ausprobiert, aber sie hat bei mir nicht funktioniert. Mit den Zeilen Response.StatusCode = ###;wurden die integrierten MVC-Fehlerseiten bei angezeigt C:\inetpub\custerr\en-US. Mir hat auch die Idee nicht gefallen, HttpHandler oder Controller manuell über meine Application_Error () -Methode aufzurufen. Ich bin froh, dass Sie eine Lösung für Ihr Problem gefunden haben. Ich weiß, welche Kopfschmerzen mir das bereitet hat.
Jesse Webb
0

Dieser Blogeintrag hat mir geholfen:

http://asp-net.vexedlogic.com/2011/04/23/asp-net-maximum-request-length-exceeded/

Wenn Sie IIS 7.0 oder höher verwenden, können Sie Ihre Web.config-Datei ändern, um zu große Anforderungen zu verarbeiten. Es gibt einige Einschränkungen, aber hier ist ein Beispiel:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="1048576" />
    </requestFiltering>
  </security>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="13" />
    <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="/UploadTooLarge.htm" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

Weitere Details zu diesen Konfigurationsdateielementen finden Sie hier:

http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits

Der Statuscode 404.13 ist als "Inhaltslänge zu groß" definiert. Eine wichtige Sache zu beachten ist, dass die maxAllowedContentLengthin Bytes angegeben wird. Dies unterscheidet sich von der maxRequestLengthEinstellung in dem <system.web>Abschnitt, der in Kilobyte angegeben ist.

<system.web>
  <httpRuntime maxRequestLength="10240" />
</system.web>

Beachten Sie auch, dass das pathAttribut ein absoluter Pfad sein muss, wenn dies der Fall responseModeist Redirect. Stellen Sie daher gegebenenfalls den Namen des virtuellen Verzeichnisses voran. Die informativen Antworten von Jesse Webb zeigen, wie man das macht responseMode="ExecuteURL", und ich würde denken, dass dieser Ansatz auch gut funktionieren würde.

Dieser Ansatz funktioniert nicht, wenn Sie mit dem Visual Studio Development Server (Cassini, dem in Visual Studio integrierten Webserver) entwickeln. Ich gehe davon aus, dass es in IIS Express funktionieren würde, aber das habe ich nicht getestet.

Dan Jagnow
quelle
0

Ich hatte das gleiche Problem, bei dem ich Application_Error()nicht getroffen wurde. Ich habe alles versucht, bis ich endlich durchging, was geschah. Ich hatte einen benutzerdefinierten Code in einem ELMAH-Ereignis, der JSON zu der gesendeten E-Mail hinzufügte, und es gab einen Nullfehler darin!

Durch die Behebung des internen Fehlers konnte der Code Application_Error()wie erwartet mit dem Ereignis fortfahren .

Matt Kemp
quelle