Обработка ошибок в ASP.NET приложении.
В данной статье рассказывается о нескольких основных способах обработки ошибок в 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") + "\n\nURL: " + Request.Path +
"\n\nQUERY: " + Request.QueryString + "\n\nMESSAGE: " +
ex.Message + "\n\nBROWSER: " + Request.Browser.Browser +
"\n\nIP Address: " + Request.UserHostAddress;
//Добавляем информацию о предыдущей посещенной странице
if(Context.Request.UrlReferrer != null)
{
Message += "\n\nReferer: " +
Context.Request.UrlReferrer.ToString();
}
//Добавляем информацию о пользователе, в случае если успешно прошел процесс аутентификации
if(Context.User.Identity.IsAuthenticated)
{
Message += "\n\nUser: " + Context.User.Identity.Name;
}
Message += "\n\n\n\nEXCEPTION: " + 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_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application. Его имя должно совпадать с указанным в коде. В нашем случае это 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>
|