Пользовательская страница ошибок ASP.NET - Server.GetLastError () имеет значение null

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

<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 () имеет значение null

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

ASP.NET 3.5 в Vista / IIS7


person nailitdown    schedule 05.12.2008    source источник
comment
Применимо также к ASP.NET 4.0 на Win7 с Cassini   -  person Marcel    schedule 20.11.2013
comment
добавить ‹customErrors mode = RemoteOnly defaultRedirect = ~ / errors / GeneralError.aspx redirectMode = ResponseRewrite /› в качестве подтвержденного ответа   -  person elle0087    schedule 03.12.2019


Ответы (10)


Если присмотреться к моей настройке web.config, можно увидеть один из комментариев в этот пост очень полезен

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

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

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

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

person nailitdown    schedule 05.12.2008
comment
У меня это не сработало. Информация об исключении потеряна. Я бы сохранил его в сеансе в Application_Error () и вытащил обратно в обработчике Page_Load () моей страницы ошибки. - person BrianK; 07.08.2009
comment
Это должно быть нормой во всей документации. Это так хорошо, что я больше не вижу причин поддерживать прежнее поведение. Пока код состояния верен, не должно быть проблем с сохранением исходного URL-адреса запроса (без перенаправления браузера). Фактически, это более правильно в соответствии с HTTP, потому что код ответа относится к запрошенному URL, а не к общему запросу страницы ошибки. Спасибо за указатель. Я пропустил эту новую функцию! - person Tony Wall; 05.02.2013
comment
Это не работает с исключениями, вызванными элементами управления внутри UpdatePanels; страница с ошибкой больше не будет отображаться. - person Sam; 23.05.2013
comment
так как это старый ответ, добавляющий мой комментарий, чтобы доказать, что это значение Nice one ResponseRewrite в режиме перенаправления работает в Asp.Net 4.5 - person Sundara Prabu; 04.02.2016

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

с этой очень наглядной схемой:

диаграмма
(источник: microsoft.com)

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

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

person nailitdown    schedule 05.12.2008

Комбинация того, что сказали 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 ...
}
person rlb.usa    schedule 05.12.2008
comment
Если вы сохраните его в приложении, как насчет всех остальных пользователей системы. Разве это не должно быть в сеансе? - person BrianK; 07.08.2009
comment
действительно, хранить это в Application [TheException] - очень плохой подход. - person Junior Mayhé; 07.07.2010
comment
Кроме того, если вы хотите поддерживать несколько вкладок для каждого пользователя, вы можете указать исключению уникальный ключ в хранилище сеансов, а затем включить этот ключ в качестве параметра строки запроса при перенаправлении на страницу с ошибкой. - person Anders Fjeldstad; 18.04.2011
comment
+1 Но имейте в виду, что Application[] - глобальный объект. Теоретически у вас может быть состояние гонки, когда вторая страница перезаписывает ошибку. Однако, поскольку Session[] не всегда доступен в условиях ошибки, я думаю, что это лучший выбор. - person Andomar; 26.03.2013
comment
Просто добавьте новый префикс GUID к ключу, используемому для хранения исключения, и передайте GUID в качестве параметра на настраиваемую страницу ошибки. - person SteveGSD; 29.05.2015
comment
Если 100 пользователей находятся в сети и одновременно выдают 100 ошибок, не стоит ли использовать Application [TheException]? - person Kiquenet; 08.09.2015
comment
@Andomar Session [] не всегда доступен в условиях ошибки? который ? - person Kiquenet; 22.12.2020

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

Затем из Page_Load() ErrorPage.aspx.cs вы можете сделать что-то вроде: Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer(), кажется, держит исключение.

person Community    schedule 23.04.2009
comment
Вот как это сделало мое приложение, и оно неплохо сработало для 99% ошибок. Но сегодня я столкнулся с исключением, которое возникает на этапе рендеринга. Если вы Server.Transfer после того, как страница наполовину отрисована, тогда HTML-код страницы, на которую вы переходите, просто объединяется с тем, что уже было отрисовано. Таким образом, вы можете получить половину неработающей страницы, за которой последует страница с ошибкой под ней. - person Kevin; 08.01.2013
comment
По какой-то причине вызов Server.Transfer () вызывает проблемы, и ошибка вообще не отображается. Следовательно, я не рекомендую использовать этот метод. Просто используйте строку web.config, как было предложено выше (‹customErrors mode = RemoteOnly defaultRedirect = ~ / errors / GeneralError.aspx redirectMode = ResponseRewrite /›), и она отлично работает - person Naresh Mittal; 16.12.2015

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

person Phil    schedule 07.06.2011
comment
В моем случае мне нужна информация об исключении только для внутреннего использования, но это хороший совет. - person nailitdown; 21.06.2011
comment
Не отвечает на вопрос. - person Arne Evertsson; 06.10.2015

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

В 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;
        }
    }
person user825345    schedule 28.09.2011
comment
Выглядит красиво, и я пробую. Итак, я ввел неправильный URL-адрес своей веб-страницы, чтобы отобразить 404 и загрузить страницу с ошибкой, но ошибки не обнаружено. Я думаю, что по какой-то причине ServerGetLastError() ничего не получает в IIS 10. - person bgmCoder; 06.11.2020
comment
Перенаправление на ~ / ErrorPages / Oops.aspx Действителен при нескольких ошибках в веб-приложении для нескольких пользователей одновременно? - person Kiquenet; 22.12.2020

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

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

P.S. Небезопасно хранить данные в коллекции Application без предварительной блокировки, а затем разблокировки.

person Leonard Lobel    schedule 05.06.2010
comment
Это будет только в том случае, если вы выполняете перенаправление на стороне клиента. При переносе на сервер все это часть одного запроса, поэтому application_error - ›page_load будет последовательно происходить на одном сервере фермы. - person davewasthere; 15.06.2017

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


in ~\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

person M.R.T    schedule 22.01.2017

Это связано с этими двумя темами ниже. Я хочу получить как GetHtmlErrorMessage, так и Session на странице ошибки.

Сеанс является нулевым после 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

person Bao    schedule 15.12.2016
comment
Если в сети есть 100 пользователей, которые одновременно выдали 100 ошибок, использование Application [TheException] не годится. - person Kiquenet; 24.09.2020

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

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

person Victor    schedule 05.12.2008