Обработка ошибок в ASP.NET приложении.

Содержание

Проблема

Во время выполнения приложения ASP.NET может возникнуть исключение, которое не обрабатывается в коде приложения, т.к. от ошибок или невнимательности никто не застрахован. 🙂 Но стандартная страница ASP.NET, сообщая об ошибке, выглядит достаточно пугающе для рядового пользователя. Решением может выступить создание для нее дружественного интерфейса.

Решение

Для этого придется внести изменения в Global.asax. Данный файл содержит методы, обрабатывающие события уровня приложения и сессии. Нам нужно будет работать с методом Application_Error. Кроме того, нужно будет создать страницу, назовем ее Error.aspx, сообщающую о возникшей ошибке.

Код Global.asax.cs

Здесь возможно несколько подходов к реализации процесса оповещения об ошибке.

1. Сообщить пользователю об ошибке и предоставить информацию о ней. В данном случае код выглядит так:

protected void Application_Error(Object sender, EventArgs e)
{
        try 
        {
                //ловим последнее возникшее исключение
                Exception lastError = Server.GetLastError();

                if (lastError != null)
                {
                        //Записываем непосредственно исключение, вызвавшее данное, в
                        //Session для дальнейшего использования
                        Session["ErrorException"] = lastError.InnerException;
                }

                // Обнуление ошибки на сервере
                Server.ClearError();

                // Перенаправление на свою страницу отображения ошибки
                Response.Redirect("Error.aspx");
        }
        catch (Exception) 
        {
                // если мы всёже приходим сюда - значит обработка исключения 
                // сама сгенерировала исключение, мы ничего не делаем, чтобы
                // не создать бесконечный цикл
                Response.Write("К сожалению произошла критическая ошибка. Нажмите кнопку 'Назад' в браузере и попробуйте ещё раз. ");
        }
}

2. Сообщить администратору

2.1. При помощи электроннной почты

protected void Application_Error(Object sender, EventArgs e)
{
        try
        {
                try 
                {
                        System.Exception ex = Server.GetLastError();
                        // Собираем необходимые данные
                        String Message = "Main Error" + "nDate & Time: " + 
                        DateTime.Now.ToString("F") + "nnURL: " + Request.Path + 
                        "nnQUERY: " + Request.QueryString + "nnMESSAGE: " +
                         ex.Message + "nnBROWSER: " + Request.Browser.Browser +
                         "nnIP Address: " + Request.UserHostAddress;

                        //Добавляем информацию о предыдущей посещенной странице
                        if(Context.Request.UrlReferrer != null)
                        {
                                Message += "nnReferer: " +
                                 Context.Request.UrlReferrer.ToString();
                        }

                        //Добавляем информацию о пользователе, в случае если успешно прошел процесс аутентификации
                        if(Context.User.Identity.IsAuthenticated)
                        {
                                Message += "nnUser: " + Context.User.Identity.Name;
                        }

                        Message += "nnnnEXCEPTION: " + ex.ToString();
                        System.Web.Mail.MailMessage mail = new System.Web.Mail.MailMessage();
                        mail.To = "[e-mail адрес администратора]";
                        mail.Subject = "Error in the Site";
                        mail.Priority = System.Web.Mail.MailPriority.High; 
                        mail.BodyFormat = System.Web.Mail.MailFormat.Text;
                        mail.Body = Message;
                        // Здесь необходимо указать используемый SMTP сервер
                        System.Web.Mail.SmtpMail.SmtpServer="[адрес SMTP сервера]";
                        System.Web.Mail.SmtpMail.Send(mail);
                } 
                catch {}
                // Обнуление ошибки на сервере
                Server.ClearError();
                // Перенаправление на статическую html страницу, сообщающую об ошибке
                // никаких данных об произошедшей ошибке ей не передается
                Response.Redirect("Error.html");
        }
        catch
        {
                // если мы всёже приходим сюда - значит обработка исключения 
                // сама сгенерировала исключение, мы ничего не делаем, чтобы
                // не создать бесконечный цикл
                Response.Write("К сожалению произошла критическая ошибка. Нажмите кнопку 'Назад' в браузере и попробуйте ещё раз. ");
        }
}

2.2. При помощи записи сообщения в журнал событий Windows

protected void Application_Error(Object sender, EventArgs e)
{
        try
        {
                // Наименование ресурса, вызвавшего ошибку
                string EventSourceName = "ErrorSample";
                // Наименование LogView
                string logName = "Application";
                
                System.Exception ex = Server.GetLastError();

                System.Diagnostics.EventLog Log = new System.Diagnostics.EventLog(logName);
                Log.Source = EventSourceName;
                Log.WriteEntry(ex.InnerException.Message, System.Diagnostics.EventLogEntryType.Error);        
        
                // Обнуление ошибки на сервере
                Server.ClearError();
                // Перенаправление на статическую html страницу, сообщающую об ошибке
                // никаких данных об произошедшей ошибке ей не передается
                Response.Redirect("Error.html");
        }
        catch (Exception ex)
        {
                // если мы всёже приходим сюда - значит обработка исключения 
                // сама сгенерировала исключение, мы ничего не делаем, чтобы
                // не создать бесконечный цикл
                Response.Write("К сожалению произошла критическая ошибка. Нажмите кнопку 'Назад' в браузере и попробуйте ещё раз. ");
        }
}

Здесь следует заметить, что зачастую приложение ASP.NET имеет достаточно ограниченный набор прав (что, в принципе, правильно с точки зрения безопасности). В связи с этим мы не сможем программно создать Event Source или проверить его существование в журнале событий Windows, если не будем использовать имперсонацию (impersonate). Решением может выступать ручное создание Event Source. Для этого внесем в реестр новый ключ, воспользовавшись программой regedit.

Нам необходимо добавить новый ключ по адресу

HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesEventlogApplication.

Его имя должно совпадать с указанным в коде. В нашем случае это ErrorSample.

3. При помощи комбинации вышеперечисленных методов

Страница Error.aspx

Страница Error.aspx, как уже говорилось выше, должна непосредственно выводить сообщение об ошибке. Для этого добавим на нее Label и назовем его lblMessage. Обработку исключения (напомню, что мы поместили его в сессию), будем производить в методе Page_Load. Его текст приведен ниже.

private void Page_Load(object sender, System.EventArgs e)
{
        try 
        {
                // возьмем информацию об исключении из сессии
                Exception exc=(Exception)Session["ErrorException"];
                string errorMsg = exc.Message;
                string pageErrorOccured = Context.Request.UrlReferrer.ToString();
                string exceptionType = exc.GetType().ToString();
                string stackTrace = exc.StackTrace;
                
                // очистим переменную сессии
                Session["ErrorException"] = null;

                //отобразим пользователю общее сообщение об ошибке
                lblMessage.Text = " К сожалению, произошла ошибка выполнения приложения.<br/><br/>";
                lblMessage.Text =String.Format("{0} Чтобы попробовать ещё раз,
                кликните <a href='{1}'>здесь</a>.<br/><br/>",lblMessage.Text,
                pageErrorOccured);

                //добавим конкретное сообщение об
                lblMessage.Text = lblMessage.Text +
                "Error Message: " + errorMsg +"n"+
                "Page Error Occurred: " + pageErrorOccured + "n"+
                "ExceptionType: " + exceptionType +"n"+
                "Stack Trace: " + stackTrace;
        }
        catch (Exception ex) 
        {
                //если исключение вызвано кодом, написанным выше
                //выведем сообщение об ошибке и StackTrace
                lblMessage.Text = ex.Message+" "+ex.StackTrace;
        }
}

Альтернатива

Следует заметить, что намного эффективнее будет использовать один из параметров файла web.config. Это позволит быстро (без изменения кода), менять ссылку страницы с сообщениями об ошибке или вообще убрать ее, в случае необходимости. Сссылка на нее задается с помощью атрибута defaultRedirect. Кроме того, при использовании данного атрибута в коде, следует убрать строки

// Обнуление ошибки на сервере
Server.ClearError();

А необходимость в следующих строках просто теряется, так как перенаправление теперь происходит автоматически.

// Перенаправление на страницу сообщающую об ошибке
Response.Redirect("Error.aspx");

Пример настройки web.config:

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

Так же, сужествует дополнительная возможность: перенаправление на определенную страницу в зависимости от кода HTTP ошибки. Это позволяет делать параметр “error“. Его атрибут statusCode задает код ошибки, а redirect задает страницу, на которую следует перенаправить пользователя. Например, в случае, если запрашиваемый ресурс не найден (код ошибки 404), перенаправим пользователя на страницу Error404.html, оповещающая пользователя о случившемся происшествии. Web.config будет выглядить так:

<customErrors mode="On" defaultRedirect="error.aspx">
  <error statusCode="404" redirect="Error404.html"/>
</customErrors>

Комментарии (1)

ruefim 2009-08-10 22:56:12
В статье преведен код который не работает.

Во первых, Метод Page_Load страницы Error.aspx содержит оператор
string pageErrorOccured = Context.Request.UrlReferrer.ToString(); при исполнении которого генерируется ошибка т.к. значение Context.Request.UrlReferrer не определено.

Во вторых, то что содержится под заголовком "Альтернатива" так же работать не будет, по той причине что не возможно передать значение lastError.InnerException с помощью переменной Session. Вообще не понял нафига эту статью на куче сайтов опубликовали!!! Если кто несогласен обоснуйте, если сможете то думаю многим поможет.
Меню