【发布时间】:2012-03-16 16:47:57
【问题描述】:
我有一个自定义的HandleError 属性来处理 MVC 管道上的错误;我的Global.asax 上有一个protected void Application_Error(object sender, EventArgs e) 方法,它处理来自管道外部的错误。
我遇到了一个我不知道可能发生的情况;在实现 DI 时,有一个 connectionString 的依赖关系,它取自应用程序配置文件。
由于连接字符串尚不存在,创建控制器时会引发错误,这通常会触发 Application_Error 处理程序,并呈现正确的错误页面(通过将部分视图呈现为字符串并将其发送为响应,如果失败,它只会将“致命异常”写入响应。
除了在这种情况下,我得到了令人毛骨悚然的默认 ASP.NET“运行时错误”黄屏死机。告诉我:
运行时错误
描述:在 服务器。此应用程序的当前自定义错误设置可防止 查看应用程序错误的详细信息。
详细信息:启用此特定错误消息的详细信息 可在本地服务器计算机上查看,请创建一个 位于根目录中的“web.config”配置文件中的标记 当前 Web 应用程序的目录。这个标签 然后应该将其“模式”属性设置为“RemoteOnly”。启用 要在远程机器上查看的详细信息,请将“模式”设置为 “关”。
我的customErrors 中没有设置defaultRedirect,也没有转为Off,因为我不想重定向而是在用户所在的同一页面上呈现错误,避免出现不必要的重定向。
我该如何处理这样的情况?它的行为方式与控制器之外的任何其他错误不同的原因是什么?
我意识到这不太可能经常发生,但我希望能够停止 YSOD(部分是因为我想隐藏我正在使用的技术,但主要是因为它根本不漂亮也不用户友好)
我什至尝试为 UnhandledExceptions 注册一个处理程序,但它也没有触发。
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
最终产生这个的代码是:
return ConfigurationManager.ConnectionStrings[key].ConnectionString;,其中ConnectionStrings[key] 是null。
更新
这是处理应用程序错误的方式:
protected void Application_Error(object sender, EventArgs e)
{
this.HandleApplicationError(new ResourceController());
}
public static void HandleApplicationError(this HttpApplication application, BaseController controller)
{
if (application == null)
{
throw new ArgumentNullException("application");
}
if (controller == null)
{
throw new ArgumentNullException("controller");
}
application.Response.Clear();
Exception exception = application.Server.GetLastError();
LogApplicationException(application.Response, exception);
try
{
RenderExceptionViewResponse(application, exception, controller);
}
catch (Exception exceptionRenderingView) // now we're in trouble. let's be as graceful as possible.
{
RenderExceptionTextResponse(application, exceptionRenderingView);
}
finally
{
application.Server.ClearError();
}
}
private static void LogApplicationException(HttpResponse response, Exception exception)
{
if (exception is HttpException)
{
HttpException httpException = (HttpException)exception;
if (httpException.GetHttpCode() == (int)HttpStatusCode.NotFound)
{
_log.Debug(Resources.Error.WebResourceNotFound, httpException);
response.Status = Resources.Constants.NotFound;
return;
}
}
_log.Error(Resources.Error.UnhandledException, exception);
}
private static void RenderExceptionViewResponse(HttpApplication application, Exception exception, BaseController controller)
{
if (!RenderAsJsonResponse(application, Resources.User.UnhandledExceptionJson))
{
ErrorViewModel model = WebUtility.GetErrorViewModel(exception);
string result = controller.RenderViewToString(Resources.Constants.ErrorViewName, model);
application.Response.Write(result);
}
}
private static void RenderExceptionTextResponse(HttpApplication application, Exception exceptionRenderingView)
{
application.Response.Clear();
if (!RenderAsJsonResponse(application, Resources.User.FatalExceptionJson))
{
application.Response.Write(Resources.User.FatalException);
}
_log.Fatal(Resources.Error.FatalException, exceptionRenderingView);
}
private static bool RenderAsJsonResponse(HttpApplication application, string message)
{
if (application.Request.IsAjaxRequest())
{
application.Response.Status = Resources.Constants.HttpSuccess;
application.Response.ContentType = Resources.Constants.JsonContentType;
application.Response.Write(message);
return true;
}
return false;
}
这是我用来装饰基本控制器的属性:
public class ErrorHandlingAttribute : HandleErrorAttribute
{
public Type LoggerType { get; set; }
public ErrorHandlingAttribute()
: this(typeof(ErrorHandlingAttribute))
{
}
public ErrorHandlingAttribute(Type loggerType)
{
LoggerType = loggerType;
}
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled)
{
return;
}
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
OnAjaxException(filterContext);
}
else
{
OnRegularException(filterContext);
}
}
internal protected void OnRegularException(ExceptionContext filterContext)
{
Exception exception = filterContext.Exception;
ILog logger = LogManager.GetLogger(LoggerType);
logger.Error(Resources.Error.UnhandledException, exception);
filterContext.HttpContext.Response.Clear();
ErrorViewModel model = WebUtility.GetErrorViewModel(exception);
filterContext.Result = new ViewResult
{
ViewName = Resources.Constants.ErrorViewName,
ViewData = new ViewDataDictionary(model)
};
filterContext.ExceptionHandled = true;
}
internal protected void OnAjaxException(ExceptionContext filterContext)
{
Exception exception = filterContext.Exception;
ILog logger = LogManager.GetLogger(LoggerType);
logger.Error(Resources.Error.UnhandledAjaxException, exception);
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.Status = Resources.Constants.HttpSuccess;
string errorMessage = WebUtility.GetUserExceptionMessage(exception, true);
filterContext.Result = new ExceptionJsonResult(new[] { errorMessage });
filterContext.ExceptionHandled = true;
}
}
这是我的customErrors:
<customErrors mode="On" />
如您所见,这些内容非常广泛,但是在访问不存在ConnectionString 的ConnectionStrings 的情况下它们甚至不会触发;这有点令人费解。
它确实在任何包含异常或不在控制器内的异常的控制器中触发,所以我不明白为什么这种情况有什么不同。
【问题讨论】:
标签: c# asp.net-mvc-3 error-handling