Pagine di errore personalizzate su asp.net MVC3


144

Sto sviluppando un sito Web di base MVC3 e sto cercando una soluzione per la gestione degli errori e il rendering di visualizzazioni personalizzate per ogni tipo di errore. Quindi immagina di avere un controller "Error" in cui la sua azione principale è "Index" (pagina di errore generico) e questo controller avrà un paio di azioni in più per gli errori che potrebbero apparire all'utente come "Handle500" o "HandleActionNotFound".

Quindi ogni errore che può verificarsi sul sito Web può essere gestito da questo controller "Errore" (esempi: "Controller" o "Azione" non trovati, 500, 404, dbException, ecc.).

Sto usando il file Sitemap per definire i percorsi del sito Web (e non il percorso).

A questa domanda è già stata data una risposta, questa è una risposta a Gweebz

Il mio metodo finale applicationiton_error è il seguente:

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

Quali dovrebbero essere le impostazioni nel web.config per supportare questo? Probabilmente non includeresti alcuna impostazione httperrors?
philbird,

forums.asp.net/p/1782402/4894514.aspx/… ha alcuni suggerimenti utili come IE non mostrerà la tua pagina di errore se è inferiore a 512 byte
RickAndMSFT

Risposte:


201

Ecco un esempio di come gestisco gli errori personalizzati. Definisco un ErrorsControllercon azioni che gestiscono diversi errori HTTP:

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

e poi mi iscrivo per l' Application_Errorin Global.asaxe invoco questo controller:

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

4
Solo una piccola nota. Dal momento che volevo eseguire il rendering di una vista in ciascun caso (404, 500, ecc.) Su ciascun ActionResult, ho restituito una vista. Tuttavia ho provato a cercare il contenuto di Application_Error e in caso di errore viene restituita una pagina HTML statica. (Posso pubblicare il codice se qualcuno lo desidera)
John Louros

4
Non riesco a visualizzare le visualizzazioni del rasoio da utilizzare con questa soluzione su MVC3. return View (modello) ad esempio ottiene solo uno schermo vuoto.
Extrakun,

2
Aggiunto TrySkipIisCustomErrors per correggerlo per IIS7 integrato. Vedi stackoverflow.com/questions/1706934/…
Pavel Savara il

1
@ajbeaven, Executeè un metodo definito IControllernell'interfaccia. Questo non può essere protetto. Guarda il mio codice più attentamente: IController errorsController = new ErrorsController();e nota il tipo di errorsControllervariabile su cui sto invocando il Executemetodo. È di tipo IControllerquindi non c'è assolutamente nulla che ti impedisca di chiamare questo metodo. E tra l'altro è Executestato protetto sia nella classe Controller che in MVC 3, quindi non ci sono cambiamenti al riguardo.
Darin Dimitrov,

2
Risolto specificando esplicitamente il tipo di contenuto della risposta:Response.ContentType = "text/html";
ajbeaven


6

Puoi farlo anche nel file Web.Config. Ecco un esempio che funziona in IIS 7.5.

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

3

Vedo che hai aggiunto un valore di configurazione per EnableCustomErrorPagee stai anche controllando IsDebuggingEnabledper determinare se eseguire o meno la gestione degli errori.

Dal momento che esiste già una <customErrors/>configurazione in ASP.NET (che si intende esattamente per questo scopo) è più semplice dire semplicemente:

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

Quindi nella configurazione che hai inserito, <customErrors mode="RemoteOnly" />che è sicuro da distribuire in quel modo, e quando devi testare la tua pagina di errore personalizzata, la imposti su<customErrors mode="On" /> modo da poter verificare che funzioni.

Nota che devi anche verificare se HttpContext.Currentè nullo perché un'eccezione Application_Startsarà ancora suo questo metodo anche se non ci sarà un contesto attivo.


2

È possibile visualizzare una pagina di errore intuitiva con il codice di stato http corretto implementando il modulo di gestione delle eccezioni facile da usare di Jeff Atwood con una leggera modifica per il codice di stato http. Funziona senza reindirizzamenti. Sebbene il codice sia del 2004 (!), Funziona bene con MVC. Può essere configurato interamente nel tuo web.config, senza alcuna modifica del codice sorgente del progetto MVC.

La modifica richiesta per restituire lo stato HTTP originale anziché uno 200stato è descritta in questo post del forum correlato .

Fondamentalmente, in Handler.vb, puoi aggiungere qualcosa come:

' 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

0

Sto usando MVC 4.5 e ho avuto problemi con la soluzione di Darin. Nota: la soluzione di Darin è eccellente e l'ho usata per trovare la mia soluzione. Ecco la mia soluzione modificata:

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

2
Quali problemi hai riscontrato con la soluzione di Darin?
Kenny Evitt,

Non hai descritto il problema riscontrato che ha provocato una risposta contrastante.
Ivanjonas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.