Страница пользовательских ошибок 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.