Benutzerdefinierte Fehlerseiten auf asp.net MVC3

144

Ich entwickle eine MVC3-Basiswebsite und suche nach einer Lösung für die Behandlung von Fehlern und das Rendern benutzerdefinierter Ansichten für jede Art von Fehler. Stellen Sie sich also vor, ich habe einen "Fehler" -Controller, dessen Hauptaktion "Index" (allgemeine Fehlerseite) ist, und dieser Controller verfügt über einige weitere Aktionen für die Fehler, die dem Benutzer möglicherweise wie "Handle500" oder "HandleActionNotFound" angezeigt werden.

Daher kann jeder Fehler, der auf der Website auftreten kann, von diesem "Fehler" -Controller behandelt werden (Beispiele: "Controller" oder "Aktion" nicht gefunden, 500, 404, dbException usw.).

Ich verwende die Sitemap-Datei, um Website-Pfade (und nicht Routen) zu definieren.

Diese Frage wurde bereits beantwortet, dies ist eine Antwort an Gweebz

Meine letzte applicaiton_error-Methode lautet wie folgt:

protected void Application_Error() {
//while my project is running in debug mode
if (HttpContext.Current.IsDebuggingEnabled && WebConfigurationManager.AppSettings["EnableCustomErrorPage"].Equals("false"))
{
    Log.Logger.Error("unhandled exception: ", Server.GetLastError());
}
else
{
    try
    {
        var exception = Server.GetLastError();

        Log.Logger.Error("unhandled exception: ", exception);

        Response.Clear();
        Server.ClearError();
        var routeData = new RouteData();
        routeData.Values["controller"] = "Errors";
        routeData.Values["action"] = "General";
        routeData.Values["exception"] = exception;

        IController errorsController = new ErrorsController();
        var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
        errorsController.Execute(rc);
    }
    catch (Exception e)
    {
        //if Error controller failed for same reason, we will display static HTML error page
        Log.Logger.Fatal("failed to display error page, fallback to HTML error: ", e);
        Response.TransmitFile("~/error.html");
    }
}
}
John Louros
quelle
Welche Einstellungen sollten in der web.config enthalten sein, um dies zu unterstützen? Vermutlich würden Sie keine httperrors-Einstellungen hinzufügen?
Philbird
forums.asp.net/p/1782402/4894514.aspx/… hat einige nette Tipps wie IE zeigt Ihre Fehlerseite nicht an, wenn sie unter 512 Bytes ist
RickAndMSFT

Antworten:

201

Hier ist ein Beispiel, wie ich mit benutzerdefinierten Fehlern umgehe. Ich definiere ein ErrorsControllermit Aktionen, die verschiedene HTTP-Fehler behandeln:

public class ErrorsController : Controller
{
    public ActionResult General(Exception exception)
    {
        return Content("General failure", "text/plain");
    }

    public ActionResult Http404()
    {
        return Content("Not found", "text/plain");
    }

    public ActionResult Http403()
    {
        return Content("Forbidden", "text/plain");
    }
}

und dann abonniere ich den Application_Errorin Global.asaxund rufe diesen Controller auf:

protected void Application_Error()
{
    var exception = Server.GetLastError();
    var httpException = exception as HttpException;
    Response.Clear();
    Server.ClearError();
    var routeData = new RouteData();
    routeData.Values["controller"] = "Errors";
    routeData.Values["action"] = "General";
    routeData.Values["exception"] = exception;
    Response.StatusCode = 500;
    if (httpException != null)
    {
        Response.StatusCode = httpException.GetHttpCode();
        switch (Response.StatusCode)
        {
            case 403:
                routeData.Values["action"] = "Http403";
                break;
            case 404:
                routeData.Values["action"] = "Http404";
                break;
        }
    }

    IController errorsController = new ErrorsController();
    var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
    errorsController.Execute(rc);
}
Darin Dimitrov
quelle
4
Nur eine kleine Notiz. Da ich auf jedem ActionResult jeweils eine Ansicht (404, 500 usw.) rendern wollte, gab ich eine Ansicht zurück. Ich habe jedoch versucht, den Inhalt von Application_Error zu ermitteln, und im Fehlerfall wird eine statische HTML-Seite zurückgegeben. (Ich kann den Code posten, wenn jemand es wünscht)
John Louros
4
Mit dieser Lösung kann ich mit MVC3 keine Rasiermesseransichten rendern. return View (Modell) zum Beispiel erhält nur einen leeren Bildschirm.
Extrakun
2
TrySkipIisCustomErrors wurde hinzugefügt, um das Problem für integriertes IIS7 zu beheben. Siehe stackoverflow.com/questions/1706934/…
Pavel Savara
1
@ajbeaven Executeist eine in der IControllerSchnittstelle definierte Methode . Dies kann unmöglich geschützt werden. Schauen Sie sich meinen Code genauer an: IController errorsController = new ErrorsController();und beachten Sie den Typ der errorsControllerVariablen, für die ich die ExecuteMethode aufrufe . Es ist vom Typ, IControlleralso hindert Sie absolut nichts daran, diese Methode aufzurufen. Übrigens Executewurde auch in der Controller-Klasse in MVC 3 geschützt, so dass sich diesbezüglich nichts ändert.
Darin Dimitrov
2
Behoben durch explizite Angabe des Inhaltstyps der Antwort:Response.ContentType = "text/html";
Ajbeaven
6

Sie können dies auch in der Datei Web.Config tun. Hier ist ein Beispiel, das in IIS 7.5 funktioniert.

     <system.webServer>
          <httpErrors errorMode="DetailedLocalOnly" defaultResponseMode="File">
                <remove statusCode="502" subStatusCode="-1" />
                <remove statusCode="501" subStatusCode="-1" />
                <remove statusCode="412" subStatusCode="-1" />
                <remove statusCode="406" subStatusCode="-1" />
                <remove statusCode="405" subStatusCode="-1" />
                <remove statusCode="404" subStatusCode="-1" />
                <remove statusCode="403" subStatusCode="-1" />
                <remove statusCode="401" subStatusCode="-1" />
                <remove statusCode="500" subStatusCode="-1" />
                <error statusCode="500" path="/notfound.html" responseMode="ExecuteURL" />
                <error statusCode="401" prefixLanguageFilePath="" path="/500.html" responseMode="ExecuteURL" />
                <error statusCode="403" prefixLanguageFilePath="" path="/403.html" responseMode="ExecuteURL" />
                <error statusCode="404" prefixLanguageFilePath="" path="/404.html" responseMode="ExecuteURL" />
                <error statusCode="405" prefixLanguageFilePath="" path="/405.html" responseMode="ExecuteURL" />
                <error statusCode="406" prefixLanguageFilePath="" path="/406.html" responseMode="ExecuteURL" />
                <error statusCode="412" prefixLanguageFilePath="" path="/412.html" responseMode="ExecuteURL" />
                <error statusCode="501" prefixLanguageFilePath="" path="/501.html" responseMode="ExecuteURL" />
                <error statusCode="502" prefixLanguageFilePath="" path="/genericerror.html" responseMode="ExecuteURL" />
           </httpErrors>
</system.webServer>
Brett Allred
quelle
3

Ich sehe, dass Sie einen Konfigurationswert für hinzugefügt haben EnableCustomErrorPageund Sie prüfen auch IsDebuggingEnabled, ob Ihre Fehlerbehandlung ausgeführt werden soll oder nicht.

Da es <customErrors/>in ASP.NET bereits eine Konfiguration gibt (die genau für diesen Zweck gedacht ist), ist es am einfachsten zu sagen:

    protected void Application_Error()
    {
        if (HttpContext.Current == null) 
        {
                // errors in Application_Start will end up here                
        }
        else if (HttpContext.Current.IsCustomErrorEnabled)
        {
                // custom exception handling
        }
    }

Dann würden Sie in die Konfiguration, die Sie <customErrors mode="RemoteOnly" />bereitstellen möchten, diese sicher bereitstellen, und wenn Sie Ihre benutzerdefinierte Fehlerseite testen müssen, würden Sie sie festlegen, <customErrors mode="On" />damit Sie überprüfen können, ob sie funktioniert.

Beachten Sie, dass Sie auch überprüfen müssen, ob HttpContext.Currentnull ist, da eine Ausnahme in Application_Startdiese Methode weiterhin verwendet wird, obwohl kein aktiver Kontext vorhanden ist.

Simon_Weaver
quelle
2

Sie können eine benutzerfreundliche Fehlerseite mit dem richtigen http-Statuscode anzeigen, indem Sie das benutzerfreundliche Ausnahmebehandlungsmodul von Jeff Atwood mit einer geringfügigen Änderung des http-Statuscodes implementieren . Es funktioniert ohne Weiterleitungen. Obwohl der Code aus dem Jahr 2004 (!) Stammt, funktioniert er gut mit MVC. Es kann vollständig in Ihrer web.config konfiguriert werden, ohne dass sich der Quellcode des MVC-Projekts ändert.

Die Änderung, die erforderlich ist, um den ursprünglichen HTTP-Status anstelle eines 200Status zurückzugeben, wird in diesem verwandten Forumsbeitrag beschrieben .

Grundsätzlich können Sie in Handler.vb Folgendes hinzufügen:

' In the header...
Private _exHttpEx As HttpException = Nothing

' At the top of Public Sub HandleException(ByVal ex As Exception)...
HttpContext.Current.Response.StatusCode = 500
If TypeOf ex Is HttpException Then
    _exHttpEx = CType(ex, HttpException)
    HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode()
End If
Martin_W
quelle
0

Ich verwende MVC 4.5 und hatte Probleme mit Darins Lösung. Hinweis: Darins Lösung ist ausgezeichnet und ich habe sie verwendet, um meine Lösung zu finden. Hier ist meine modifizierte Lösung:

protected void Application_Error(object sender, EventArgs e)
{           
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.StatusCode = httpException.GetHttpCode();

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


if (httpException != null)
{
    var httpContext = HttpContext.Current;

    httpContext.RewritePath("/Errors/InternalError", false);

    // MVC 3 running on IIS 7+
    if (HttpRuntime.UsingIntegratedPipeline)
    {
        switch (Response.StatusCode)
        {
            case 403:
                httpContext.Server.TransferRequest("/Errors/Http403", true);
                break;
            case 404:
                httpContext.Server.TransferRequest("/Errors/Http404", true);
                break;
            default:
                httpContext.Server.TransferRequest("/Errors/InternalError", true);
                break;
        }
    }
    else
    {
        switch (Response.StatusCode)
        {
            case 403:
                httpContext.RewritePath(string.Format("/Errors/Http403", true));
                break;
            case 404:
                httpContext.RewritePath(string.Format("/Errors/Http404", true));
                break;
            default:
                httpContext.RewritePath(string.Format("/Errors/InternalError", true));
                break;
        }

        IHttpHandler httpHandler = new MvcHttpHandler();
        httpHandler.ProcessRequest(httpContext);
    }
}
}
MVCdragon
quelle
2
Welche Probleme hatten Sie mit Darins Lösung?
Kenny Evitt
Sie haben das aufgetretene Problem nicht beschrieben, das zu einer konkurrierenden Antwort geführt hat.
Iwanjonas