Benutzerdefinierte ASP.NET-Fehlerseite - Server.GetLastError () ist null

112

Ich habe eine benutzerdefinierte Fehlerseite für meine Anwendung eingerichtet:

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

In Global.asax, Application_Error (), werden mit dem folgenden Code die Ausnahmedetails abgerufen:

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

Wenn ich zu meiner Fehlerseite (~ / Errors / GeneralError.aspx.cs) komme, ist Server.GetLastError () null

Gibt es eine Möglichkeit, die Ausnahmedetails auf der Fehlerseite und nicht in Global.asax.cs abzurufen?

ASP.NET 3.5 unter Vista / IIS7

Nailitdown
quelle
Gilt auch für ASP.NET 4.0 unter Win7 mit Cassini
Marcel
Fügen Sie "<customErrors mode =" RemoteOnly "defaultRedirect =" ~ / Errors / GeneralError.aspx "redirectMode =" ResponseRewrite "/>" als bestätigte Antwort hinzu
elle0087

Antworten:

137

Bei genauerer Betrachtung meiner web.config-Einrichtung ist einer der Kommentare in diesem Beitrag sehr hilfreich

In asp.net 3.5 sp1 gibt es einen neuen Parameter redirectMode

Wir können also customErrorsdiesen Parameter ändern :

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

Der ResponseRewriteModus ermöglicht es uns, die «Fehlerseite» zu laden, ohne den Browser umzuleiten, sodass die URL gleich bleibt und für mich wichtig ist, dass Ausnahmeinformationen nicht verloren gehen.

Nailitdown
quelle
4
Das hat bei mir nicht funktioniert. Die Ausnahmeinformationen gehen verloren. Ich würde es in der Sitzung in Application_Error () speichern und im Page_Load () - Handler meiner Fehlerseite wieder herausziehen.
BrianK
2
Das sollte in allen Unterlagen die Norm sein. Das ist so gut, dass ich keinen Grund mehr sehe, das alte Verhalten zu unterstützen. Solange der Statuscode korrekt ist, sollte es kein Problem geben, die ursprüngliche Anforderungs-URL intakt zu lassen (keine Browserumleitung). Tatsächlich ist dies laut HTTP korrekter, da sich der Antwortcode auf die angeforderte URL bezieht und nicht auf eine gemeinsame Fehlerseitenanforderung. Vielen Dank für den Hinweis, dass ich diese neue Funktion verpasst habe!
Tony Wall
Dies funktioniert nicht mit Ausnahmen, die durch Steuerelemente in UpdatePanels ausgelöst werden . Die Fehlerseite wird nicht mehr angezeigt.
Sam
2
da es eine alte Antwort ist, die meinen Kommentar hinzufügt, um zu beweisen, dass dieser nette ResponseRewrite-Wert im Redirect-Modus in Asp.Net 4.5 funktioniert
Sundara Prabu
38

OK, ich habe diesen Beitrag gefunden: http://msdn.microsoft.com/en-us/library/aa479319.aspx

mit diesem sehr anschaulichen Diagramm:

Diagramm
(Quelle: microsoft.com )

Um diese Ausnahmedetails zu erhalten, muss ich sie im Wesentlichen selbst in Global.asax speichern, damit sie später auf meiner benutzerdefinierten Fehlerseite abgerufen werden können.

Es scheint der beste Weg zu sein, den Großteil der Arbeit in Global.asax zu erledigen, wobei die benutzerdefinierten Fehlerseiten eher hilfreiche Inhalte als Logik behandeln.

Nailitdown
quelle
18

Eine Kombination aus dem, was NailItDown und Victor gesagt haben. Der bevorzugte / einfachste Weg besteht darin, den Fehler mit Global.Asax zu speichern und dann auf Ihre benutzerdefinierte Fehlerseite umzuleiten.

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
}

Außerdem müssen Sie Ihre web.config einrichten :

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

Und schließlich tun Sie alles, was Sie brauchen, mit der Ausnahme, die Sie auf Ihrer Fehlerseite gespeichert haben :

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 ...
}
rlb.usa
quelle
35
Wenn Sie es in der Anwendung speichern, was ist mit allen anderen Benutzern des Systems? Sollte es nicht in der Sitzung sein?
BrianK
11
in der Tat ist das ein wirklich schlechter Ansatz, um dies in der Anwendung ["TheException"] zu speichern
Junior Mayhé
4
Wenn Sie mehrere "Registerkarten" pro Benutzer unterstützen möchten, möchten Sie der Ausnahme möglicherweise einen eindeutigen Schlüssel im Sitzungsspeicher zuweisen und diesen Schlüssel dann als Querystring-Parameter einschließen, wenn Sie zur Fehlerseite umleiten.
Anders Fjeldstad
5
+1 Aber sei dir bewusst, dass dies Application[]ein globales Objekt ist. Theoretisch könnte es zu einer Rennbedingung kommen, bei der eine zweite Seite den Fehler überschreibt. Da dies Session[]jedoch unter Fehlerbedingungen nicht immer verfügbar ist, halte ich dies für die bessere Wahl.
Andomar
3
Fügen Sie dem Schlüssel, in dem die Ausnahme gespeichert ist, einfach ein neues GUID-Präfix hinzu und übergeben Sie die GUID als Parameter an die benutzerdefinierte Fehlerseite.
SteveGSD
6

Versuchen Sie, etwas Server.Transfer("~/ErrorPage.aspx");aus der Application_Error()Methode von global.asax.cs zu verwenden

Dann Page_Load()sollten Sie innerhalb von ErrorPage.aspx.cs in Ordnung sein, um Folgendes zu tun:Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() scheint die Ausnahme herumzuhängen.


quelle
So hat es meine Anwendung gemacht und es hat bei 99% der Fehler ganz gut funktioniert. Aber heute bin ich auf eine Ausnahme gestoßen, die während des Rendering-Schritts auftritt. Wenn Sie Server.Transfereine Seite zur Hälfte gerendert haben, wird der HTML-Code der Seite, auf die Sie übertragen, einfach mit dem verknüpft, was bereits gerendert wurde. Es kann also vorkommen, dass Sie eine halbe kaputte Seite haben, gefolgt von der Fehlerseite darunter.
Kevin
Aus irgendeinem Grund führt der Aufruf von Server.Transfer () zu Problemen und Fehler werden überhaupt nicht angezeigt. Daher empfehle ich diese Methode nicht. Verwenden Sie einfach die Zeile web.config wie oben vorgeschlagen (<customErrors mode = "RemoteOnly" defaultRedirect = "~ / error / GeneralError.aspx" redirectMode = "ResponseRewrite" />) und es funktioniert einwandfrei
Naresh Mittal
5

Obwohl es hier einige gute Antworten gibt, muss ich darauf hinweisen, dass es nicht empfehlenswert ist, Systemausnahmemeldungen auf Fehlerseiten anzuzeigen (was ich davon ausgehe, dass Sie dies tun möchten). Sie können böswilligen Benutzern versehentlich Dinge offenlegen, die Sie nicht möchten. Beispielsweise sind SQL Server-Ausnahmemeldungen sehr ausführlich und können den Benutzernamen, das Kennwort und die Schemainformationen der Datenbank angeben, wenn ein Fehler auftritt. Diese Informationen sollten einem Endbenutzer nicht angezeigt werden.

Phil
quelle
1
In meinem Fall wollte ich nur die Ausnahmeinformationen für die Back-End-Verwendung, aber das ist ein guter Rat.
Nailitdown
2
Beantwortet die Frage nicht.
Arne Evertsson
5

Hier ist meine Lösung ..

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;
        }
    }
user825345
quelle
4

Eine wichtige Überlegung, die meiner Meinung nach hier allen fehlt, ist ein Lastausgleichsszenario (Webfarm). Da sich der Server, auf dem global.asax ausgeführt wird, möglicherweise von dem Server unterscheidet, auf dem die benutzerdefinierte Fehlerseite ausgeführt wird, ist das Speichern des Ausnahmeobjekts in der Anwendung nicht zuverlässig.

Ich bin immer noch auf der Suche nach einer zuverlässigen Lösung für dieses Problem in einer Webfarmkonfiguration und / oder einer guten Erklärung von MS, warum Sie die Ausnahme mit Server.GetLastError auf der benutzerdefinierten Fehlerseite nicht wie möglich abrufen können in global.asax Application_Error.

PS Es ist nicht sicher, Daten in der Anwendungssammlung zu speichern, ohne sie zuerst zu sperren und dann zu entsperren.

Leonard Lobel
quelle
Dies ist nur dann der Fall, wenn Sie eine clientseitige Umleitung durchführen. Wenn Sie eine Serverübertragung durchführen, ist alles Teil einer Anforderung, sodass application_error -> page_load nacheinander auf dem einen Server in der Farm ausgeführt wird.
Davewasthere
2

Dies bezog sich auf diese beiden Themen unten. Ich möchte sowohl GetHtmlErrorMessage als auch Session on Error-Seite erhalten.

Die Sitzung ist nach ResponseRewrite null

Warum ist HttpContext.Session null, wenn redirectMode = ResponseRewrite

Ich habe versucht, eine Lösung zu finden, die nicht nötig ist Server.Transfer() or Response.Redirect()

Erstens: Entfernen Sie ResponseRewrite in web.config

Web.config

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

Dann Global.asax

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

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

Für Referenzen

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

Bao
quelle
2

Es hat bei mir funktioniert. in MVC 5


im ~\Global.asax

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


in ~\ControllersErstellenErrorController.cs

using System.Web.Mvc;

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


in ~\ModelsErstellenFunctionTools.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 ~\ViewsOrdner erstellen Error und in ~\Views\ErrorErstellenError.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>
    }
}


Wenn Sie diese Adresse eingeben localhost/Error Seite öffnen ohne Fehler



Und wenn ein Fehler auftritt Fehler tritt auf

Anstatt Fehler anzuzeigen, muss die Variable 'log' in der Datenbank gespeichert werden


Quelle: Microsoft ASP.Net

MRT2017
quelle
1

Ich denke, Sie haben hier ein paar Möglichkeiten.

Sie können die letzte Ausnahme in der Sitzung speichern und von Ihrer benutzerdefinierten Fehlerseite abrufen. oder Sie können einfach innerhalb des Application_error-Ereignisses zu Ihrer benutzerdefinierten Fehlerseite umleiten. Wenn Sie sich für Letzteres entscheiden, möchten Sie sicherstellen, dass Sie die Server.Transfer-Methode verwenden.

Sieger
quelle