HandleError
属性,用于处理MVC管道上的错误; 我在我的Global.asax
上有一个protected void Application_Error(object sender, EventArgs e)
方法,可以处理来自管道外部的错误。我遇到了一种我不知道可能发生的情况; 在实现DI时,存在一个
connectionString
的依赖项,该依赖项从应用程序配置文件中获取。由于连接字符串尚不存在,因此在创建控制器时会引发错误,这通常会使
Application_Error
处理程序触发,并呈现适当的错误页面(通过将部分视图呈现为字符串并将其作为响应发送,以防此失败,它只是向响应中写入“致命异常。”)。除了在这种情况下,我得到了丑陋的默认ASP.NET“运行时错误”黄色屏幕。告诉我:
``` 运行时错误
说明: 服务器上发生应用程序错误。该应用程序的当前自定义错误设置阻止查看应用程序错误的详细信息。
详细信息: 要使本特定错误消息的详细信息可在本地服务器计算机上查看,请在当前Web应用程序的根目录中创建一个名为“web.config”的配置文件中的标记。然后,此标记应将其“mode”属性设置为“RemoteOnly”。要使详细信息可以在远程机器上查看,请将“mode”设置为“Off”。 ```
我没有在
customErrors
中设置defaultRedirect
,也没有将其设置为Off
,因为我不想重定向,而是希望在用户所在的同一页上呈现错误,避免不必要的重定向。我该如何处理这种情况?为什么它的行为方式与控制器外部的任何其他错误不同?
我意识到这种情况不太可能经常发生,但我希望能够停止YSOD(部分是因为我想隐藏我正在使用的技术,但主要是因为它不太美观,也不太用户友好)。 我甚至尝试注册一个未处理异常的处理程序,但它也没有触发。
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
的情况,它们甚至不会触发;这有点令人困惑。
它在任何包含异常的控制器或不在控制器中的异常中都会触发,所以我不明白为什么这种情况不同。