Страница пользовательских ошибок ASP.NET - сервер.GetLastError() является нулевым

У меня есть настраиваемая страница ошибки, настроенная для моего приложения:

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

В Global.asax, Application_Error(), следующий код работает для получения сведений об исключении:

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

К тому времени, когда я добираюсь до своей страницы ошибок (~/errors/GeneralError.aspx.cs), Server.GetLastError() является нулевым

Можно ли как-то получить информацию об исключении на странице ошибок, а не в Global.asax.cs?

ASP.NET 3.5 в Vista/IIS7

10 ответов

Решение

При более внимательном рассмотрении моей настройки web.config, один из комментариев в этом посте очень полезен.

в asp.net 3.5 sp1 появился новый параметр redirectMode

Таким образом, мы можем изменить customErrors добавить этот параметр:

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

ResponseRewrite Режим позволяет нам загружать "страницу ошибок" без перенаправления браузера, поэтому URL остается прежним, и, что важно для меня, информация об исключениях не теряется.

Хорошо, я нашел этот пост: http://msdn.microsoft.com/en-us/library/aa479319.aspx

с этой очень иллюстративной диаграммой:

http://i.msdn.microsoft.com/Aa479319.customerrors_01(en-us,MSDN.10).gif

по сути, чтобы получить подробности об этих исключениях, мне нужно сохранить их самостоятельно в Global.asax для последующего поиска на моей пользовательской странице ошибок.

Похоже, что лучший способ - выполнить большую часть работы в Global.asax, когда пользовательские страницы ошибок обрабатывают полезный контент, а не логику.

Сочетание того, что сказал NailItDown и Виктор. Предпочтительный / самый простой способ - использовать ваш Global.Asax для хранения ошибки, а затем перенаправить на свою страницу ошибки.

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
}

Кроме того, вам нужно настроить ваш web.config:

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

И, наконец, делайте все, что вам нужно, за исключением того, что вы сохранили на странице ошибок:

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

Попробуйте использовать что-то вроде Server.Transfer("~/ErrorPage.aspx"); изнутри Application_Error() метод global.asax.cs

Тогда изнутри Page_Load() ErrorPage.aspx.cs вы должны быть в порядке, чтобы сделать что-то вроде: Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() кажется, что исключение висит вокруг.

Вот мое решение..

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

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

Хотя здесь есть несколько хороших ответов, я должен отметить, что не рекомендуется отображать сообщения об исключениях системы на страницах ошибок (что, как я полагаю, вы хотите сделать). Вы можете непреднамеренно сообщать злоумышленникам о том, что вы не хотите делать. Например, сообщения об исключениях Sql Server очень многословны и могут содержать имя пользователя, пароль и информацию о схеме базы данных при возникновении ошибки. Эта информация не должна отображаться для конечного пользователя.

Одним из важных соображений, которое, как мне кажется, всем здесь не хватает, является сценарий балансировки нагрузки (веб-фермы). Поскольку сервер, выполняющий global.asax, может отличаться от сервера, на котором выполняется страница пользовательской ошибки, сохранение объекта исключения в приложении ненадежно.

Я все еще ищу надежное решение этой проблемы в конфигурации веб-фермы и / или хорошее объяснение от MS о том, почему вы просто не можете подобрать исключение с помощью Server.GetLastError на пользовательской странице ошибок, как вы можете в global.asax Application_Error.

PS Хранить данные в коллекции приложений небезопасно, не заблокировав и не разблокировав их.

Это сработало для меня. в MVC 5


в ~\Global.asax

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


в ~\Controllers Создайте ErrorController.cs

using System.Web.Mvc;

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


в ~\Models Создайте FunctionTools.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;
        }
    }
}


в ~\Views Создать папку Error И в ~\Views\Error Создайте Error.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>
    }
}


Если вы введете этот адрес localhost/Error открыть страницу без ошибок



И если происходит ошибка происходит ошибка

Как и вместо отображения ошибок, переменная 'log' будет храниться в базе данных


Источник: Microsoft ASP.Net

Это связано с этими 2 темами ниже, я хочу получить как GetHtmlErrorMessage, так и Session on Error page.

Сессия является нулевой после ResponseRewrite

Почему HttpContext.Session имеет значение null, когда redirectMode = ResponseRewrite

Я попытался найти решение, которое не нужно Server.Transfer() or Response.Redirect()

Во-первых: удалите ResponseRewrite в web.config

Web.config

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

Тогда Global.asax

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

Тогда 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
            }

Для справок

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

Я думаю, у вас есть несколько вариантов здесь.

вы можете сохранить последнее исключение в сеансе и получить его со своей пользовательской страницы ошибок; или вы можете просто перенаправить на свою страницу ошибок в событии Application_error. Если вы выберете последнее, вы должны убедиться, что используете метод Server.Transfer.

Другие вопросы по тегам