Come far funzionare le pagine di errore personalizzate in ASP.NET MVC 4


247

Voglio una pagina di errore personalizzata per 500, 404 e 403. Ecco cosa ho fatto:

  1. Abilitato gli errori personalizzati in web.config come segue:

    <customErrors mode="On" 
                  defaultRedirect="~/Views/Shared/Error.cshtml">
    
        <error statusCode="403" 
               redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />
    
        <error statusCode="404" 
               redirect="~/Views/Shared/FileNotFound.cshtml" />
    
    </customErrors>
  2. Registrato HandleErrorAttributecome filtro di azione globale nella FilterConfigclasse come segue:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }
  3. Creata una pagina di errore personalizzata per ciascuno dei messaggi di cui sopra. Quello predefinito per 500 era già disponibile.

  4. Dichiarato in ogni visualizzazione di pagina di errore personalizzata che è il modello per la pagina System.Web.Mvc.HandleErrorInfo

Per 500, mostra la pagina di errore personalizzata. Per altri no.

C'è qualcosa che mi manca?

Sembra che questo non sia tutto ciò che c'è da mostrare errori personalizzati mentre leggo il codice nel OnExceptionmetodo della HandleErrorAttributeclasse e ne gestisce solo 500.

Cosa devo fare per gestire altri errori?


21
La cosa strana di questa configurazione è che il reindirizzamento alle visualizzazioni, non alle azioni del controller. Chi dovrebbe rendere tali viste e passare in un modello, per esempio? Solo pensando.
Oliver,

2
La maggior parte delle risposte qui non gestisce tutti i casi o fa sì che il server Web risponda in modo "errato", ovvero reindirizzando a una pagina di errore anziché restituire una risposta di errore. Se ti interessa che il server risponda nel modo previsto dai server Web, qui è disponibile un articolo abbastanza dettagliato al riguardo: benfoster.io/blog/aspnet-mvc-custom-error-pages . Tieni presente che non è così semplice come le risposte qui, quindi se vuoi una risposta semplice usa invece una di quelle qui sotto.
rans

1
Ecco un altro grande articolo su varie tecniche per la gestione degli errori asp.net dusted.codes/…
Godsayah

Risposte:


352

La mia configurazione attuale (su MVC3, ma penso che sia ancora valida) si basa sull'avere un ErrorController, quindi uso:

<system.web>
    <customErrors mode="On" defaultRedirect="~/Error">
      <error redirect="~/Error/NotFound" statusCode="404" />
    </customErrors>
</system.web>

E il controller contiene quanto segue:

public class ErrorController : Controller
{
    public ViewResult Index()
    {
        return View("Error");
    }
    public ViewResult NotFound()
    {
        Response.StatusCode = 404;  //you may want to set this to 200
        return View("NotFound");
    }
}

E le viste nel modo in cui le implementate. Tendo ad aggiungere un po 'di logica, per mostrare la traccia dello stack e le informazioni di errore se l'applicazione è in modalità debug. Quindi Error.cshtml è simile al seguente:

@model System.Web.Mvc.HandleErrorInfo
@{
    Layout = "_Layout.cshtml";
    ViewBag.Title = "Error";
}
<div class="list-header clearfix">
    <span>Error</span>
</div>
<div class="list-sfs-holder">
    <div class="alert alert-error">
        An unexpected error has occurred. Please contact the system administrator.
    </div>
    @if (Model != null && HttpContext.Current.IsDebuggingEnabled)
    {
        <div>
            <p>
                <b>Exception:</b> @Model.Exception.Message<br />
                <b>Controller:</b> @Model.ControllerName<br />
                <b>Action:</b> @Model.ActionName
            </p>
            <div style="overflow:scroll">
                <pre>
                    @Model.Exception.StackTrace
                </pre>
            </div>
        </div>
    }
</div>

7
Hai dovuto mettere qualcosa nel tuo Application_Error nel tuo Global.asax per questo Pablo?
Alicia,

12
Il codice nel controller non sembra essere eseguito dalla mia esperienza. MVC4: il lancio di System.Exception in un controller diverso renderà il rendering del file Error.cshtml, ma non tramite ErrorController. Qualcun altro sta vivendo questo?
Nilzor,

53
Per chiunque lo abbia trovato utile, ma necessitasse di più contesto; Il tag <customErrors> si trova all'interno di <system.web> in web.config.
gooberverse,

7
Aggiornamento per gli altri - a quanto pare il mio problema stava accadendo perché avevo redirectMode="ResponseRewrite"sul CustomerErrorselemento
KyleMit

42
Per favore, per amore di Dio, ignora il commento che dice //you may want to set this to 200nel codice. NON FARLO!
Demenza

40

Ho fatto la soluzione pablo e ho sempre avuto l'errore (MVC4)

La vista "Errore" o il relativo master non sono stati trovati o nessun motore di visualizzazione supporta la posizione cercata.

Per sbarazzarsi di questo, rimuovere la linea

 filters.Add(new HandleErrorAttribute());

in FilterConfig.cs


Ho cercato ovunque per risolvere questo. Questo alla fine ebbe la risposta. Sapevo perché lo stava facendo ma per me non ci riuscivo, senza pensare drasticamente a quello che hanno detto gli altri. Immagino di condividere il dolore di 360Airwalk quando ti ringrazio per averlo sottolineato. Leggenda!
Adam,

Questa è un'opzione e il controller di errore funziona correttamente. Sembra che quando registri i filtri in FilterConfig.cs, cerca Error.cshtml nelle cartelle di visualizzazione dei controller condivisi e originali. Quando cambi Error.cshtml in qualcosa di diverso da quello che funziona il nostro ErrorController personalizzato. Ma c'è un posto dove puoi aggiungere questa registrazione ed è global.asax.cs. Se aggiungi la linea menzionata nella funzione RegisterGlobalFilters (filtri GlobalFilterCollection) in global.asax.cs e rimuovi da FilterConfig.cs, funziona.
isaolmez,

Penso che sia correlato all'ordine delle registrazioni dei filtri. Mantenere il controller degli errori e spostare la registrazione del filtro su global.asax.cs. vuoto statico pubblico RegisterGlobalFilters (filtri GlobalFilterCollection) {filters.Add (new HandleErrorAttribute ()); }
isaolmez,

24

Faccio qualcosa che richiede meno codifica rispetto alle altre soluzioni pubblicate.

Innanzitutto, nel mio web.config, ho il seguente:

<customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
   <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
   <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
</customErrors>

E il controller (/Controllers/ErrorPageController.cs) contiene quanto segue:

public class ErrorPageController : Controller
{
    public ActionResult Oops(int id)
    {
        Response.StatusCode = id;

        return View();
    }
}

Infine, la vista contiene quanto segue (ridotto per semplicità, ma può contenere:

@{ ViewBag.Title = "Oops! Error Encountered"; }

<section id="Page">
  <div class="col-xs-12 well">
    <table cellspacing="5" cellpadding="3" style="background-color:#fff;width:100%;" class="table-responsive">
      <tbody>
        <tr>
          <td valign="top" align="left" id="tableProps">
            <img width="25" height="33" src="~/Images/PageError.gif" id="pagerrorImg">
          </td>
          <td width="360" valign="middle" align="left" id="tableProps2">
            <h1 style="COLOR: black; FONT: 13pt/15pt verdana" id="errortype"><span id="errorText">@Response.Status</span></h1>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth"><font style="COLOR: black; FONT: 8pt/11pt verdana">Possible causes:</font>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth2">
            <font style="COLOR: black; FONT: 8pt/11pt verdana" id="LID1">
                            <hr>
                            <ul>
                                <li id="list1">
                                    <span class="infotext">
                                        <strong>Baptist explanation: </strong>There
                                        must be sin in your life. Everyone else opened it fine.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Presbyterian explanation: </strong>It's
                                        not God's will for you to open this link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong> Word of Faith explanation:</strong>
                                        You lack the faith to open this link. Your negative words have prevented
                                        you from realizing this link's fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Charismatic explanation: </strong>Thou
                                        art loosed! Be commanded to OPEN!<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Unitarian explanation:</strong> All
                                        links are equal, so if this link doesn't work for you, feel free to
                                        experiment with other links that might bring you joy and fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Buddhist explanation:</strong> .........................<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Episcopalian explanation:</strong>
                                        Are you saying you have something against homosexuals?<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Christian Science explanation: </strong>There
                                        really is no link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Atheist explanation: </strong>The only
                                        reason you think this link exists is because you needed to invent it.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Church counselor's explanation:</strong>
                                        And what did you feel when the link would not open?
                                    </span>
                                </li>
                            </ul>
                            <p>
                                <br>
                            </p>
                            <h2 style="font:8pt/11pt verdana; color:black" id="ietext">
                                <img width="16" height="16" align="top" src="~/Images/Search.gif">
                                HTTP @Response.StatusCode - @Response.StatusDescription <br>
                            </h2>
                        </font>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</section>

È così semplice. Potrebbe essere facilmente esteso per offrire informazioni più dettagliate sull'errore, ma ELMAH gestisce questo per me e lo statusCode e statusDescription è tutto ciò di cui di solito ho bisogno.


Penso che il reindirizzamento nel file .config di "~ / ErrorPage / Oops / 404" probabilmente dovrebbe essere "~ / ErrorPage / Oops? 404" giusto? Almeno questo è ciò che ha funzionato per me. Forse dipende solo dal routing.
Josh Sutterfield,

Come simulare un errore generato da IIS. Che si tratti di 500 o 504. Cosa fare nel codice ASP.Net MVC - 5 per simulare l'eccezione da IIS in modo da poter testare la mia pagina di errore personalizzata
Infrangibile

12

Sembra che ci siano una serie di passaggi qui confusi. Presenterò ciò che ho fatto da zero.

  1. Crea il ErrorPagecontroller

    public class ErrorPageController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    
        public ActionResult Oops(int id)
        {
            Response.StatusCode = id;
            return View();
        }
    }
  2. Aggiungi visualizzazioni per queste due azioni (clic destro -> Aggiungi visualizzazione). Questi dovrebbero apparire in una cartella chiamata ErrorPage.

  3. All'interno App_Startapri FilterConfig.cse commenta il filtro di gestione degli errori.

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // Remove this filter because we want to handle errors ourselves via the ErrorPage controller
        //filters.Add(new HandleErrorAttribute());
    }
  4. All'interno di web.config aggiungi le seguenti <customerErrors>voci, sottoSystem.Web

    <customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
        <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
        <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
    </customErrors>
  5. Test (ovviamente). Generare un'eccezione non gestita nel codice e vederlo andare alla pagina con ID 500, quindi utilizzare un URL per una pagina che non esiste per vedere 404.


Stavo ricevendo questo errore An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.Tutto quello che ho raccolto dal tuo codice è nel file web.config, l'ho aggiunto <error redirect = "~/ControllerName/ActionName" statusCode="404"/>e ha funzionato bene :) Il resto del codice è stato dalla risposta di @ Pablo. Sto usando MVC 5 e il framework entità 6. Non ho rimosso filters.Add(new HandleErrorAttribute())daFilterConfig.cs
sumedha

Come simulare un errore generato da IIS. Che si tratti di 500 o 504. Cosa fare nel codice ASP.Net MVC - 5 per simulare l'eccezione da IIS in modo da poter testare la mia pagina di errore personalizzata
Infrangibile

Inoltre, come generare un'eccezione non gestita (passaggio 5). Sono nuovo di programmazione, per favore guida.
Infrangibile il

Non funziona ancora per me? E il routing? Devo aggiungere anche la pagina Routing for Error? Se colpisco page: localhost: 84 / Enforcer / blah vengo reindirizzato a: localhost: 84 / Enforcer / Enforcer / Error / NotFound? Aspxerrorpath = /… La pagina di errore appare come una pagina di errore standard fornita da Asp.NET. Qualche idea?
Radek Strugalski,

L'elemento customerrors nel webconfig dovrebbe essere questo problema. Il codice di percorso predefinito (creato dal progetto) dovrebbe funzionare correttamente.
VictorySaber,

11

Consiglierei di utilizzare il file Global.asax.cs.

 protected void Application_Error(Object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    if (exception is HttpUnhandledException)
    {
        Server.Transfer("~/Error.aspx");
    }
    if (exception != null)
    {
        Server.Transfer("~/Error.aspx");
    }
    try
    {
        // This is to stop a problem where we were seeing "gibberish" in the
        // chrome and firefox browsers
        HttpApplication app = sender as HttpApplication;
        app.Response.Filter = null;
    }
    catch
    {
    }
}

1
Non pensavo che potresti fare un Server.Transfer () in MVC. Pensi che l'OP abbia un sito misto?
Rap

1
perché dovremmo usare Application_Error in mvc? Abbiamo opzioni come l'attributo [handleerror] con le opzioni url di reindirizzamento. C'è qualche vantaggio specifico con application_error a questo?
Kurkula,

Dovremmo usare HandleErrorAttribute in MVC e sovrascrivendo il metodo OnException, possiamo gestirli in un modo molto migliore
Kumar Lachhani,

7

Sulla base della risposta inviata da maxspan, ho creato un progetto di esempio minimo su GitHub che mostra tutte le parti funzionanti.

Fondamentalmente, aggiungiamo semplicemente un Application_Errormetodo a global.asax.cs per intercettare l'eccezione e darci l'opportunità di reindirizzare (o più correttamente, trasferire la richiesta ) a una pagina di errore personalizzata.

    protected void Application_Error(Object sender, EventArgs e)
    {
        // See http://stackoverflow.com/questions/13905164/how-to-make-custom-error-pages-work-in-asp-net-mvc-4
        // for additional context on use of this technique

        var exception = Server.GetLastError();
        if (exception != null)
        {
            // This would be a good place to log any relevant details about the exception.
            // Since we are going to pass exception information to our error page via querystring,
            // it will only be practical to issue a short message. Further detail would have to be logged somewhere.

            // This will invoke our error page, passing the exception message via querystring parameter
            // Note that we chose to use Server.TransferRequest, which is only supported in IIS 7 and above.
            // As an alternative, Response.Redirect could be used instead.
            // Server.Transfer does not work (see https://support.microsoft.com/en-us/kb/320439 )
            Server.TransferRequest("~/Error?Message=" + exception.Message);
        }

    }

Controller errori:

/// <summary>
/// This controller exists to provide the error page
/// </summary>
public class ErrorController : Controller
{
    /// <summary>
    /// This action represents the error page
    /// </summary>
    /// <param name="Message">Error message to be displayed (provided via querystring parameter - a design choice)</param>
    /// <returns></returns>
    public ActionResult Index(string Message)
    {
        // We choose to use the ViewBag to communicate the error message to the view
        ViewBag.Message = Message;
        return View();
    }

}

Pagina errore Visualizza:

<!DOCTYPE html>

<html>
<head>
    <title>Error</title>
</head>
<body>

    <h2>My Error</h2>
    <p>@ViewBag.Message</p>
</body>
</html>

Nient'altro è coinvolto, tranne la disabilitazione / rimozione filters.Add(new HandleErrorAttribute())in FilterConfig.cs

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //filters.Add(new HandleErrorAttribute()); // <== disable/remove
    }
}

Sebbene molto semplice da implementare, l'unico inconveniente che vedo in questo approccio è l'utilizzo della stringa di query per fornire informazioni sull'eccezione alla pagina di errore di destinazione.


3

Avevo impostato tutto, ma non riuscivo ancora a visualizzare le pagine di errore corrette per il codice di stato 500 sul nostro server di gestione temporanea, nonostante tutto funzionasse correttamente sui server di sviluppo locale.

Ho trovato questo post sul blog di Rick Strahl che mi ha aiutato.

Ho dovuto aggiungere Response.TrySkipIisCustomErrors = true;al mio codice di gestione degli errori personalizzato.


@ Shaun314 Vuoi dire dove metti quel codice? Nell'azione che gestisce la richiesta. Puoi vedere esempi in quel post sul blog.
DCShannon,

2

Ecco la mia soluzione Usa [ExportModelStateToTempData] / [ImportModelStateFromTempData] è scomodo secondo me.

~ / Vista / Home / Error.cshtml:

@{
    ViewBag.Title = "Error";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Error</h2>
<hr/>

<div style="min-height: 400px;">

    @Html.ValidationMessage("Error")

    <br />
    <br />

    <button onclick="Error_goBack()" class="k-button">Go Back</button>
    <script>
        function Error_goBack() {
            window.history.back()
        }
    </script>

</div>

~ / Controller / HomeController.sc:

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Error()
    {
        return this.View();
    }

    ...
}

~ / Controller / BaseController.sc:

public class BaseController : Controller
{
    public BaseController() { }

    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            if (filterContext.Controller.TempData.ContainsKey("Error"))
            {
                var modelState = filterContext.Controller.TempData["Error"] as ModelState;
                filterContext.Controller.ViewData.ModelState.Merge(new ModelStateDictionary() { new KeyValuePair<string, ModelState>("Error", modelState) });
                filterContext.Controller.TempData.Remove("Error");
            }
        }
        if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
        {
            if (filterContext.Controller.ViewData.ModelState.ContainsKey("Error"))
            {
                filterContext.Controller.TempData["Error"] = filterContext.Controller.ViewData.ModelState["Error"];
            }
        }

        base.OnActionExecuted(filterContext);
    }
}

~ / Controller / MyController.sc:

public class MyController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Details(int id)
    {
        if (id != 5)
        {
            ModelState.AddModelError("Error", "Specified row does not exist.");
            return RedirectToAction("Error", "Home");
        }
        else
        {
            return View("Specified row exists.");
        }
    }
}

Ti auguro progetti di successo ;-)


2

Puoi ottenere errori che funzionano correttamente senza hackerare global.cs, fare scherzi con HandleErrorAttribute, fare Response. TrySkipIisCustomErrors, collegare Application_Error o qualsiasi altra cosa:

In system.web (solo il solito, on / off)

<customErrors mode="On">
  <error redirect="/error/401" statusCode="401" />
  <error redirect="/error/500" statusCode="500" />
</customErrors>

e in system.webServer

<httpErrors existingResponse="PassThrough" />

Ora le cose dovrebbero comportarsi come previsto e puoi usare ErrorController per visualizzare tutto ciò di cui hai bisogno.


Come simulare un errore generato da IIS. Che si tratti di 500 o 504. Cosa fare nel codice ASP.Net MVC - 5 per simulare l'eccezione da IIS in modo da poter testare la mia pagina di errore personalizzata
Infrangibile

@Unbreakable modifica temporaneamente il codice per generare un'eccezione.
chiamato il

Non ha fatto differenza per me. Non sono portato alla mia pagina di errore personalizzata in caso di un'eccezione o errore 404 non trovato.
pnizzle,

0

Sembra che sia arrivato tardi alla festa, ma dovresti anche dare un'occhiata.

Quindi, system.webper memorizzare nella cache eccezioni all'interno dell'applicazione come return HttpNotFound ()

  <system.web>
    <customErrors mode="RemoteOnly">
      <error statusCode="404" redirect="/page-not-found" />
      <error statusCode="500" redirect="/internal-server-error" />
    </customErrors>
  </system.web>

e system.webServerper recuperare errori che sono stati rilevati da IIS e non si sono fatti strada verso il framework asp.net

 <system.webServer>
    <httpErrors errorMode="DetailedLocalOnly">
      <remove statusCode="404"/>
      <error statusCode="404" path="/page-not-found" responseMode="Redirect"/>
      <remove statusCode="500"/>
      <error statusCode="500" path="/internal-server-error" responseMode="Redirect"/>
  </system.webServer>

Nell'ultimo, se ti preoccupi della risposta del client, modifica responseMode="Redirect"in responseMode="File"e pubblica un file html statico, poiché questo visualizzerà una pagina descrittiva con un codice di 200 risposte.


0

In web.config aggiungilo sotto il tag system.webserver come di seguito,

<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404"/>
  <remove statusCode="500"/>
  <error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound"/>
  <error statusCode="500" responseMode="ExecuteURL"path="/Error/ErrorPage"/>
</httpErrors>

e aggiungi un controller come,

public class ErrorController : Controller
{
    //
    // GET: /Error/
    [GET("/Error/NotFound")]
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;

        return View();
    }

    [GET("/Error/ErrorPage")]
    public ActionResult ErrorPage()
    {
        Response.StatusCode = 500;

        return View();
    }
}

e aggiungere le loro opinioni rispettate, questo funzionerà sicuramente credo per tutti.

Questa soluzione l'ho trovata da: Neptune Century

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.