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

112

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

<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

гвоздь
источник
Применимо также к ASP.NET 4.0 на Win7 с Cassini
Марсель
добавить "<customErrors mode =" RemoteOnly "defaultRedirect =" ~ / errors / GeneralError.aspx "redirectMode =" ResponseRewrite "/>" в качестве подтвержденного ответа
elle0087

Ответы:

137

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

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

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

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

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

гвоздь
источник
4
У меня это не сработало. Информация об исключении потеряна. Я бы сохранил его в сеансе в Application_Error () и вытащил обратно в обработчике Page_Load () моей страницы ошибки.
BrianK 07
2
Это должно быть нормой во всей документации. Это так хорошо, что я больше не вижу причин поддерживать прежнее поведение. Пока код состояния верен, не должно возникнуть проблем с сохранением исходного URL-адреса запроса (без перенаправления браузера). Фактически, это более правильно в соответствии с HTTP, потому что код ответа относится к запрошенному URL, а не к общему запросу страницы ошибки. Спасибо за указатель. Я пропустил эту новую функцию!
Тони Уолл
Это не работает с исключениями, вызванными элементами управления внутри UpdatePanels; страница с ошибкой больше не будет отображаться.
Сэм
2
так как это старый ответ, добавляющий мой комментарий, чтобы доказать, что это значение Nice one ResponseRewrite в режиме перенаправления работает в Asp.Net 4.5
Сундара Прабу
38

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

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

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

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

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

гвоздь
источник
18

Комбинация того, что сказали 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 ...
}
rlb.usa
источник
35
Если вы храните его в приложении, как насчет всех остальных пользователей системы. Разве это не должно быть в сеансе?
Брайан К.
11
действительно, это действительно плохой подход - хранить это в приложении ["TheException"]
Джуниор Мэйхе,
4
Кроме того, если вы хотите поддерживать несколько «вкладок» для каждого пользователя, вы можете указать исключению уникальный ключ в хранилище сеансов, а затем включить этот ключ в качестве параметра строки запроса при перенаправлении на страницу с ошибкой.
Андерс Фьельдстад
5
+1 Но знайте, что Application[]это глобальный объект. Теоретически у вас может быть состояние гонки, когда вторая страница перезаписывает ошибку. Однако, поскольку Session[]это не всегда доступно в условиях ошибки, я думаю, что это лучший выбор.
Andomar
3
Просто добавьте новый префикс GUID к ключу, используемому для хранения исключения, и передайте GUID в качестве параметра на настраиваемую страницу ошибки.
SteveGSD
6

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

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

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


источник
Вот как это сделало мое приложение, и оно неплохо сработало для 99% ошибок. Но сегодня я столкнулся с исключением, которое возникает на этапе рендеринга. Если вы Server.Transferпосле того, как страница была отрисована наполовину, то HTML-код страницы, на которую вы переходите, просто объединяется с тем, что уже было отрисовано. Таким образом, вы можете получить половину неработающей страницы, за которой следует страница с ошибкой под ней.
Кевин
По какой-то причине вызов Server.Transfer () вызывает проблемы, и ошибка вообще не отображается. Следовательно, я не рекомендую использовать этот метод. Просто используйте строку web.config, как предложено выше (<customErrors mode = "RemoteOnly" defaultRedirect = "~ / errors / GeneralError.aspx" redirectMode = "ResponseRewrite" />), и она отлично работает
Нареш Миттал
5

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

Фил
источник
1
В моем случае мне нужна информация об исключении только для внутреннего использования, но это хороший совет.
nailitdown
2
Не отвечает на вопрос.
Арне Эвертссон
5

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

В 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;
        }
    }
user825345
источник
4

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

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

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

Леонард Лобель
источник
Это будет только в том случае, если вы выполняете перенаправление на стороне клиента. При переносе на сервер все это часть одного запроса, поэтому application_error -> page_load все будет происходить на одном сервере в ферме последовательно.
davewasthere
2

Это связано с этими двумя темами ниже. Я хочу получить как 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

Бао
источник
2

У меня это сработало. в MVC 5


в ~\Global.asax

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


в ~\ControllersCreateErrorController.cs

using System.Web.Mvc;

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


в ~\ModelsCreateFunctionTools.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

MRT2017
источник
1

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

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

Виктор
источник