Niestandardowa strona błędu ASP.NET - Server.GetLastError () ma wartość null

112

Mam skonfigurowaną niestandardową stronę błędu dla mojej aplikacji:

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

W Global.asax, Application_Error (), poniższy kod działa w celu uzyskania szczegółów wyjątku:

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

Zanim dojdę do mojej strony błędu (~ / errors / GeneralError.aspx.cs), wartość Server.GetLastError () ma wartość null

Czy jest jakiś sposób, aby uzyskać szczegółowe informacje o wyjątku na stronie błędu, a nie w Global.asax.cs?

ASP.NET 3.5 w systemie Vista / IIS7

Nailitdown
źródło
Dotyczy również ASP.NET 4.0 na Win7 z Cassini
Marcel
dodaj „<customErrors mode =" RemoteOnly "defaultRedirect =" ~ / errors / GeneralError.aspx "redirectMode =" ResponseRewrite "/>" jako potwierdzoną odpowiedź
elle0087

Odpowiedzi:

137

Przyglądając się bliżej mojej konfiguracji web.config, jeden z komentarzy w tym poście jest bardzo pomocny

w asp.net 3.5 sp1 jest nowy parametr redirectMode

Więc możemy zmienić, customErrorsaby dodać ten parametr:

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

ResponseRewritetryb pozwala nam załadować «błąd Strona» bez przekierowanie przeglądarki, więc pobytów URL takie same, a najważniejsze dla mnie informacje wyjątek nie jest stracone.

Nailitdown
źródło
4
To nie zadziałało dla mnie. Informacje o wyjątku zostały utracone. Chciałbym przechowywać go w sesji w Application_Error () i wyciągnąć go z powrotem w module obsługi Page_Load () mojej strony błędu.
BrianK
2
To powinno być normą w całej dokumentacji. To jest tak dobre, że nie widzę już powodu, aby wspierać stare zachowanie. Dopóki kod stanu jest poprawny, nie powinno być problemu z pozostawieniem pierwotnego adresu URL żądania w stanie nienaruszonym (bez przekierowania przeglądarki). W rzeczywistości jest to bardziej poprawne według HTTP, ponieważ kod odpowiedzi odnosi się do żądanego adresu URL, a nie do udostępnionego żądania strony błędu. Dzięki za wskazówkę, że przegapiłem tę nową funkcję!
Tony Wall
To nie działa z wyjątkami wyzwalanymi przez kontrolki wewnątrz UpdatePanels; strona błędu nie będzie już wyświetlana.
Sam
2
ponieważ jest to stara odpowiedź, dodając mój komentarz, aby udowodnić, że to Nice one Wartość ResponseRewrite w trybie redirectmode działa w Asp.Net 4.5
Sundara Prabu
38

OK, znalazłem ten post: http://msdn.microsoft.com/en-us/library/aa479319.aspx

z tym bardzo ilustracyjnym diagramem:

diagram
(źródło: microsoft.com )

w istocie, aby uzyskać szczegółowe informacje o wyjątkach, muszę je samodzielnie przechowywać w Global.asax, aby później móc je odzyskać na mojej niestandardowej stronie błędu.

wydaje się, że najlepszym sposobem jest wykonanie większości pracy w Global.asax, z niestandardowymi stronami błędów obsługującymi pomocne treści, a nie logikę.

Nailitdown
źródło
18

Połączenie tego, co powiedzieli NailItDown i Victor. Preferowanym / najłatwiejszym sposobem jest użycie pliku Global.Asax do zapisania błędu, a następnie przekierowanie do niestandardowej strony błędu.

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
}

Ponadto musisz skonfigurować swój web.config :

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

I na koniec zrób wszystko, co musisz, z wyjątkiem zapisanym na stronie błędu :

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
źródło
35
Jeśli przechowujesz to w aplikacji, co z pozostałymi użytkownikami systemu. Czy nie powinno być na sesji?
BrianK
11
rzeczywiście, przechowywanie tego w aplikacji [„TheException”] jest naprawdę złym podejściem
Junior Mayhé
4
Ponadto, jeśli chcesz obsługiwać wiele „zakładek” na użytkownika, możesz chcieć nadać wyjątkowi unikalny klucz w magazynie sesji, a następnie dołączyć ten klucz jako parametr zapytania podczas przekierowywania na stronę błędu.
Anders Fjeldstad
5
+1 Ale pamiętaj, że Application[]jest to obiekt globalny. Teoretycznie możesz mieć sytuację wyścigu, w której druga strona nadpisuje błąd. Ponieważ jednak Session[]nie zawsze jest dostępny w warunkach błędu, myślę, że jest to lepszy wybór.
Andomar
3
Wystarczy dodać nowy prefiks GUID do klucza używanego do przechowywania wyjątku i przekazać identyfikator GUID jako parametr do niestandardowej strony błędu.
SteveGSD
6

Spróbuj użyć czegoś podobnego Server.Transfer("~/ErrorPage.aspx");do Application_Error()metody global.asax.cs

Następnie z poziomu Page_Load()ErrorPage.aspx.cs powinieneś być w stanie zrobić coś takiego:Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() wydaje się, że wyjątek kręci się wokół.


źródło
Tak zrobiła to moja aplikacja i działała całkiem dobrze przy 99% błędów. Ale dzisiaj natknąłem się na wyjątek, który występuje podczas renderowania. Jeśli Server.Transferstrona jest renderowana w połowie, kod HTML strony, na którą się przenosisz, jest po prostu łączony z tym, co zostało już wyrenderowane. Możesz więc skończyć z połową zepsutej strony, a następnie stroną błędu poniżej.
Kevin,
Z jakiegoś powodu wywołanie Server.Transfer () powoduje problemy i błąd w ogóle nie jest wyświetlany. Dlatego nie polecam korzystania z tej metody. Po prostu użyj wiersza web.config zgodnie z powyższą sugestią (<customErrors mode = "RemoteOnly" defaultRedirect = "~ / errors / GeneralError.aspx" redirectMode = "ResponseRewrite" />) i działa dobrze
Naresh Mittal
5

Chociaż jest tutaj kilka dobrych odpowiedzi, muszę zwrócić uwagę, że wyświetlanie komunikatów o wyjątkach systemowych na stronach błędów (jak zakładam, jest tym, co chcesz zrobić) nie jest dobrą praktyką. Możesz nieumyślnie ujawnić szkodliwym użytkownikom rzeczy, których nie chcesz. Na przykład komunikaty o wyjątkach serwera Sql są bardzo szczegółowe i mogą podawać nazwę użytkownika, hasło i informacje o schemacie bazy danych, gdy wystąpi błąd. Informacje te nie powinny być wyświetlane użytkownikowi końcowemu.

Phil
źródło
1
W moim przypadku chciałem tylko informacje o wyjątku do użytku końcowego, ale to dobra rada.
nailitdown
2
Nie odpowiada na pytanie.
Arne Evertsson
5

Oto moje rozwiązanie ...

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

W 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
źródło
4

Jedną z ważnych kwestii, której moim zdaniem wszystkim brakuje, jest scenariusz równoważenia obciążenia (farma internetowa). Ponieważ serwer, który wykonuje global.asax, może być inny niż serwer, który ma wykonać niestandardową stronę błędu, przechowywanie obiektu wyjątku w aplikacji nie jest niezawodne.

Nadal szukam niezawodnego rozwiązania tego problemu w konfiguracji farmy internetowej i / lub dobrego wyjaśnienia od MS, dlaczego po prostu nie możesz odebrać wyjątku za pomocą Server.GetLastError na niestandardowej stronie błędu, tak jak możesz w global.asax Application_Error.

PS Przechowywanie danych w kolekcji aplikacji bez ich uprzedniego zablokowania, a następnie odblokowania jest niebezpieczne.

Leonard Lobel
źródło
Dzieje się tak tylko wtedy, gdy wykonujesz przekierowanie po stronie klienta. Kiedy wykonujesz transfer serwera, to wszystko jest częścią jednego żądania, więc application_error -> page_load będą miały miejsce po kolei na jednym serwerze w farmie.
davewasthere
2

Jest to związane z tymi 2 tematami poniżej, chcę uzyskać zarówno GetHtmlErrorMessage, jak i stronę Session on Error.

Sesja ma wartość null po ResponseRewrite

Dlaczego HttpContext.Session ma wartość null, gdy redirectMode = ResponseRewrite

Próbowałem i widziałem rozwiązanie, którego nie potrzebowałem Server.Transfer() or Response.Redirect()

Po pierwsze: usuń ResponseRewrite w web.config

Web.config

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

Następnie Global.asax

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

Następnie 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
            }

W celach informacyjnych

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

Bao
źródło
2

U mnie to zadziałało. w MVC 5


w ~\Global.asax

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


w ~\ControllersUtwórzErrorController.cs

using System.Web.Mvc;

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


w ~\ModelsUtwórzFunctionTools.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;
        }
    }
}


w ~\ViewsUtwórz folder Error i ~\Views\ErrorUtwórzError.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>
    }
}


Jeśli wpiszesz ten adres localhost/Error otwórz stronę Whithout Error



A jeśli wystąpi błąd występuje błąd

Jak może być zamiast wyświetlać błędy, zmienna „log” ma być przechowywana w bazie danych


Źródło: Microsoft ASP.Net

MRT2017
źródło
1

Myślę, że masz tutaj kilka opcji.

możesz zapisać ostatni wyjątek w sesji i pobrać go ze swojej niestandardowej strony błędu; lub możesz po prostu przekierować do niestandardowej strony błędu w zdarzeniu Application_error. Jeśli wybierzesz tę drugą opcję, upewnij się, że korzystasz z metody Server.Transfer.

Zwycięzca
źródło