Pagina di errore personalizzata ASP.NET: Server.GetLastError () è null


112

Ho una pagina di errore personalizzata impostata per la mia applicazione:

<customErrors mode="On" defaultRedirect="~/errors/GeneralError.aspx"
/>

In Global.asax, Application_Error (), il codice seguente funziona per ottenere i dettagli dell'eccezione:

  Exception ex = Server.GetLastError();
  if (ex != null)
    {
        if (ex.GetBaseException() != null)
            ex = ex.GetBaseException();
    }

Quando arrivo alla mia pagina di errore (~ / errors / GeneralError.aspx.cs), Server.GetLastError () è nullo

Esiste un modo per ottenere i dettagli dell'eccezione nella pagina di errore, anziché in Global.asax.cs?

ASP.NET 3.5 su Vista / IIS7


Si applica anche su ASP.NET 4.0 su Win7 con Cassini
Marcel

aggiungi "<customErrors mode =" RemoteOnly "defaultRedirect =" ~ / errors / GeneralError.aspx "redirectMode =" ResponseRewrite "/>" come risposta confermata
elle0087

Risposte:


137

Guardando più da vicino la mia configurazione web.config, uno dei commenti in questo post è molto utile

in asp.net 3.5 sp1 è presente un nuovo parametro redirectMode

Quindi possiamo modificare customErrorsper aggiungere questo parametro:

<customErrors mode="RemoteOnly" defaultRedirect="~/errors/GeneralError.aspx" redirectMode="ResponseRewrite" />

la ResponseRewritemodalità ci permette di caricare la «Pagina di errore» senza reindirizzare il browser, quindi l'URL rimane lo stesso e, cosa importante per me, le informazioni sull'eccezione non vengono perse.


4
Questo non ha funzionato per me. Le informazioni sull'eccezione vengono perse. Vorrei memorizzarlo nella sessione in Application_Error () e tirarlo nuovamente fuori nel gestore Page_Load () della mia pagina di errore.
BrianK

2
Questa dovrebbe essere la norma in tutta la documentazione. Questo è così buono che non vedo più motivo per supportare il vecchio comportamento. Finché il codice di stato è corretto, non dovrebbero esserci problemi a lasciare intatto l'URL della richiesta originale (senza eseguire un reindirizzamento del browser). In realtà ciò è più corretto secondo HTTP perché il codice di risposta si riferisce all'URL richiesto, non a una richiesta di pagina di errore condivisa. Grazie per il puntatore, mi sono perso quella nuova funzionalità!
Tony Wall

Questo non funziona con le eccezioni attivate dai controlli all'interno di UpdatePanels; la pagina di errore non verrà più visualizzata.
Sam

2
dal momento che è una vecchia risposta che aggiunge il mio commento per dimostrare che questa bella risposta Il valore di
riscrittura

38

OK, ho trovato questo post: http://msdn.microsoft.com/en-us/library/aa479319.aspx

con questo diagramma molto illustrativo:

diagramma
(fonte: microsoft.com )

in sostanza, per ottenere quei dettagli sull'eccezione, ho bisogno di memorizzarli io stesso in Global.asax, per un successivo recupero sulla mia pagina di errore personalizzata.

sembra che il modo migliore sia svolgere la maggior parte del lavoro in Global.asax, con le pagine di errore personalizzate che gestiscono contenuti utili anziché logica.


18

Una combinazione di ciò che hanno detto NailItDown e Victor. Il modo preferito / più semplice è utilizzare Global.Asax per memorizzare l'errore e quindi reindirizzare alla tua pagina di errore personalizzata.

Global.asax :

    void Application_Error(object sender, EventArgs e) 
{
    // Code that runs when an unhandled error occurs
    Exception ex = Server.GetLastError();
    Application["TheException"] = ex; //store the error for later
    Server.ClearError(); //clear the error so we can continue onwards
    Response.Redirect("~/myErrorPage.aspx"); //direct user to error page
}

Inoltre, devi configurare il tuo web.config :

  <system.web>
    <customErrors mode="RemoteOnly" defaultRedirect="~/myErrorPage.aspx">
    </customErrors>
  </system.web>

E infine, fai tutto ciò che ti serve con l'eccezione che hai memorizzato nella tua pagina di errore :

protected void Page_Load(object sender, EventArgs e)
{

    // ... do stuff ...
    //we caught an exception in our Global.asax, do stuff with it.
    Exception caughtException = (Exception)Application["TheException"];
    //... do stuff ...
}

35
Se lo memorizzi nell'applicazione, che dire di tutti gli altri utenti del sistema. Non dovrebbe essere nella sessione?
BrianK

11
in effetti, è davvero un pessimo approccio memorizzare questo nell'applicazione ["TheException"]
Junior Mayhé

4
Inoltre, se si desidera supportare più "schede" per utente, è possibile assegnare all'eccezione una chiave univoca nell'archivio della sessione e quindi includere tale chiave come parametro della stringa di query quando si reindirizza alla pagina di errore.
Anders Fjeldstad

5
+1 Ma sappi che Application[]è un oggetto globale. Teoricamente potresti avere una condizione di competizione in cui una seconda pagina sovrascrive l'errore. Tuttavia, poiché Session[]non è sempre disponibile in condizioni di errore, penso che questa sia la scelta migliore.
Andomar

3
Basta aggiungere un nuovo prefisso GUID alla chiave utilizzata per memorizzare l'eccezione e passare il GUID come parametro alla pagina di errore personalizzata.
SteveGSD

6

Prova a utilizzare qualcosa di simile Server.Transfer("~/ErrorPage.aspx");all'interno del Application_Error()metodo global.asax.cs

Quindi dall'interno Page_Load()di ErrorPage.aspx.cs dovresti essere a posto per fare qualcosa come:Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() sembra mantenere l'eccezione in giro.


È così che la mia applicazione ha funzionato e ha funzionato abbastanza bene per il 99% degli errori. Ma oggi mi sono imbattuto in un'eccezione che si verifica durante la fase di rendering. Se Server.Transferdopo che una pagina è stata renderizzata a metà, l'HTML della pagina in cui trasferisci viene semplicemente concatenato a tutto ciò che è già stato renderizzato. Quindi potresti ritrovarti con mezza pagina interrotta seguita dalla pagina di errore sottostante.
Kevin

Per qualche motivo, la chiamata a Server.Transfer () causa problemi e l'errore non viene visualizzato affatto. E quindi, non consiglio di utilizzare questo metodo. Utilizza semplicemente la riga web.config come suggerito sopra (<customErrors mode = "RemoteOnly" defaultRedirect = "~ / errors / GeneralError.aspx" redirectMode = "ResponseRewrite" />) e funziona bene
Naresh Mittal

5

Sebbene ci siano molte buone risposte qui, devo sottolineare che non è una buona pratica visualizzare i messaggi di eccezione del sistema sulle pagine di errore (che è quello che presumo tu voglia fare). Potresti inavvertitamente rivelare cose che non desideri fare a utenti malintenzionati. Ad esempio, i messaggi di eccezione di Sql Server sono molto dettagliati e possono fornire il nome utente, la password e le informazioni sullo schema del database quando si verifica un errore. Tali informazioni non dovrebbero essere mostrate a un utente finale.


1
Nel mio caso volevo solo le informazioni sull'eccezione per l'uso di back-end, ma è un buon consiglio.
nailitdown

2
Non risponde alla domanda.
Arne Evertsson

5

Ecco la mia soluzione ..

In Global.aspx:

void Application_Error(object sender, EventArgs e)
    {
        // Code that runs when an unhandled error occurs

        //direct user to error page 
        Server.Transfer("~/ErrorPages/Oops.aspx"); 
    }

In Oops.aspx:

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadError(Server.GetLastError()); 
    }

    protected void LoadError(Exception objError)
    {
        if (objError != null)
        {
            StringBuilder lasterror = new StringBuilder();

            if (objError.Message != null)
            {
                lasterror.AppendLine("Message:");
                lasterror.AppendLine(objError.Message);
                lasterror.AppendLine();
            }

            if (objError.InnerException != null)
            {
                lasterror.AppendLine("InnerException:");
                lasterror.AppendLine(objError.InnerException.ToString());
                lasterror.AppendLine();
            }

            if (objError.Source != null)
            {
                lasterror.AppendLine("Source:");
                lasterror.AppendLine(objError.Source);
                lasterror.AppendLine();
            }

            if (objError.StackTrace != null)
            {
                lasterror.AppendLine("StackTrace:");
                lasterror.AppendLine(objError.StackTrace);
                lasterror.AppendLine();
            }

            ViewState.Add("LastError", lasterror.ToString());
        }
    }

   protected void btnReportError_Click(object sender, EventArgs e)
    {
        SendEmail();
    }

    public void SendEmail()
    {
        try
        {
            MailMessage msg = new MailMessage("webteam", "webteam");
            StringBuilder body = new StringBuilder();

            body.AppendLine("An unexcepted error has occurred.");
            body.AppendLine();

            body.AppendLine(ViewState["LastError"].ToString());

            msg.Subject = "Error";
            msg.Body = body.ToString();
            msg.IsBodyHtml = false;

            SmtpClient smtp = new SmtpClient("exchangeserver");
            smtp.Send(msg);
        }

        catch (Exception ex)
        {
            lblException.Text = ex.Message;
        }
    }

4

Una considerazione importante che penso manchi a tutti qui è uno scenario di bilanciamento del carico (web farm). Poiché il server che esegue global.asax potrebbe essere diverso dal server che riguarda l'esecuzione della pagina di errore personalizzata, lo stashing dell'oggetto eccezione in Application non è affidabile.

Sto ancora cercando una soluzione affidabile a questo problema in una configurazione di web farm e / o una buona spiegazione da parte di MS sul motivo per cui non puoi semplicemente raccogliere l'eccezione con Server.GetLastError nella pagina di errore personalizzata come puoi in global.asax Application_Error.

PS Non è sicuro archiviare i dati nella raccolta dell'applicazione senza prima bloccarli e poi sbloccarli.


Questo sarebbe il caso solo se stai eseguendo un reindirizzamento lato client. Quando si esegue un trasferimento dal server, fa tutto parte di un'unica richiesta, quindi application_error -> page_load avverrà tutto sull'unico server della farm, in sequenza.
davewasthere

2

Questo relativo a questi 2 argomenti di seguito, voglio ottenere sia GetHtmlErrorMessage che la pagina Session on Error.

La sessione è nulla dopo ResponseRewrite

Perché HttpContext.Session è null quando redirectMode = ResponseRewrite

Ho provato e vedere una soluzione che non serve Server.Transfer() or Response.Redirect()

Primo: rimuovere ResponseRewrite in web.config

web.config

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

Quindi Global.asax

    void Application_Error(object sender, EventArgs e)
    {
         if(Context.IsCustomErrorEnabled)
         {     
            Exception ex = Server.GetLastError();
            Application["TheException"] = ex; //store the error for later
         }
    }

Quindi errorHandler.aspx.cs

        protected void Page_Load(object sender, EventArgs e)
            {       
                string htmlErrorMessage = string.Empty ;
                Exception ex = (Exception)Application["TheException"];
                string yourSessionValue = HttpContext.Current.Session["YourSessionId"].ToString();

                //continue with ex to get htmlErrorMessage 
                if(ex.GetHtmlErrorMessage() != null){              
                    htmlErrorMessage = ex.GetHtmlErrorMessage();
                }   
                // continue your code
            }

Per riferimenti

http://www.developer.com/net/asp/article.php/3299641/ServerTransfer-Vs-ResponseRedirect.htm


2

Ha funzionato per me. in MVC 5


nel ~\Global.asax

void Application_Error(object sender, EventArgs e)
{
    FTools.LogException();
    Response.Redirect("/Error");
}


in ~\ControllersCreaErrorController.cs

using System.Web.Mvc;

namespace MVC_WebApp.Controllers
{
    public class ErrorController : Controller
    {
        // GET: Error
        public ActionResult Index()
        {
            return View("Error");
        }
    }
}


in ~\ModelsCreaFunctionTools.cs

using System;
using System.Web;

namespace MVC_WebApp.Models
{
    public static class FTools
    {
        private static string _error;
        private static bool _isError;

        public static string GetLastError
        {
            get
            {
                string cashe = _error;
                HttpContext.Current.Server.ClearError();
                _error = null;
                _isError = false;
                return cashe;
            }
        }
        public static bool ThereIsError => _isError;

        public static void LogException()
        {
            Exception exc = HttpContext.Current.Server.GetLastError();
            if (exc == null) return;
            string errLog = "";
            errLog += "**********" + DateTime.Now + "**********\n";
            if (exc.InnerException != null)
            {
                errLog += "Inner Exception Type: ";
                errLog += exc.InnerException.GetType() + "\n";
                errLog += "Inner Exception: ";
                errLog += exc.InnerException.Message + "\n";
                errLog += "Inner Source: ";
                errLog += exc.InnerException.Source + "\n";
                if (exc.InnerException.StackTrace != null)
                {
                    errLog += "\nInner Stack Trace: " + "\n";
                    errLog += exc.InnerException.StackTrace + "\n";
                }
            }
            errLog += "Exception Type: ";
            errLog += exc.GetType().ToString() + "\n";
            errLog += "Exception: " + exc.Message + "\n";
            errLog += "\nStack Trace: " + "\n";
            if (exc.StackTrace != null)
            {
                errLog += exc.StackTrace + "\n";
            }
            _error = errLog;
            _isError = true;
        }
    }
}


in ~\ViewsCrea cartella Error e in ~\Views\ErrorCreaError.cshtml

@using MVC_WebApp.Models
@{
    ViewBag.Title = "Error";
    if (FTools.ThereIsError == false)
    {
        if (Server.GetLastError() != null)
        {
            FTools.LogException();
        }
    }
    if (FTools.ThereIsError == false)
    {
        <br />
        <h1>No Problem!</h1>
    }
    else
    {
        string log = FTools.GetLastError;
        <div>@Html.Raw(log.Replace("\n", "<br />"))</div>
    }
}


Se inserisci questo indirizzo localhost/Error apri la pagina senza errori



E se si verifica un errore si verifica un errore

Come può essere invece di visualizzare gli errori, la variabile 'log' da memorizzare nel database


Fonte: Microsoft ASP.Net


1

Penso che tu abbia un paio di opzioni qui.

puoi memorizzare l'ultima eccezione nella sessione e recuperarla dalla tua pagina di errore personalizzata; oppure puoi semplicemente reindirizzare alla tua pagina di errore personalizzata all'interno dell'evento Application_error. Se scegli quest'ultimo, assicurati di utilizzare il metodo Server.Transfer.

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.