C# ASP.NET MVC 全局错误处理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9461190/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ASP.NET MVC Global error handling
提问by bevacqua
I have a custom HandleErrorattribute that deals with errors on the MVC pipeline; I have an protected void Application_Error(object sender, EventArgs e)method on my Global.asaxwhich handles errors from outside the pipeline.
我有一个自定义HandleError属性来处理 MVC 管道上的错误;我有一个protected void Application_Error(object sender, EventArgs e)方法Global.asax可以处理来自管道外部的错误。
I've come across an scenario I didn't know was possible; In implementing DI, there's a dependency for a connectionString, which is taken from the application configuration file.
我遇到了一个我不知道是可能的情况;在实现 DI 时, a 有一个依赖项connectionString,它取自应用程序配置文件。
As the connection string didn't exist yet, an error raises when creating the controller, this usually makes the Application_Errorhandler fire, and a proper error page is rendered (through rendering a partial view as string and sending it as the response, and in case this fails it just writes "Fatal exception." to the response.
由于连接字符串尚不存在,因此在创建控制器时会引发错误,这通常会Application_Error触发处理程序,并呈现正确的错误页面(通过将部分视图呈现为字符串并将其作为响应发送,以防万一这失败了,它只是在响应中写入“致命异常”。
Except in this case, I get the fugly default ASP.NET "runtime error" yellow screen of death. Telling me:
除了在这种情况下,我得到了 ASP.NET 默认的“运行时错误”黄屏死机。告诉我:
Runtime Error
Description: An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed.
Details: To enable the details of this specific error message to be viewable on the local server machine, please create a tag within a "web.config" configuration file located in the root directory of the current web application. This tag should then have its "mode" attribute set to "RemoteOnly". To enable the details to be viewable on remote machines, please set "mode" to "Off".
运行时错误
描述:服务器上发生应用程序错误。此应用程序的当前自定义错误设置阻止查看应用程序错误的详细信息。
详细信息:要在本地服务器计算机上查看此特定错误消息的详细信息,请在位于当前 Web 应用程序根目录的“web.config”配置文件中创建一个标记。然后,此标记应将其“模式”属性设置为“RemoteOnly”。要使详细信息可以在远程机器上查看,请将“模式”设置为“关闭”。
I don't have a defaultRedirectset in my customErrors, nor is it turned Off, because I don't want to redirect but to render errors on the same page the user is in, avoiding a needless redirect.
我的 中没有defaultRedirectset customErrors,也没有 turn Off,因为我不想重定向,而是在用户所在的同一页面上呈现错误,避免不必要的重定向。
How can I handle an scenario such as this? And what's even the reason why it behaves this way and not like any other error outside a controller?
我该如何处理这样的场景?甚至是什么原因导致它以这种方式运行而不像控制器之外的任何其他错误?
I realize it's not likely to happen often, but I would like being able to stop the YSOD (partly because I want to hide the technology I'm using, but mostly because it's not pretty nor user friendly at all)
我意识到它不太可能经常发生,但我希望能够停止 YSOD(部分是因为我想隐藏我正在使用的技术,但主要是因为它根本不漂亮也不用户友好)
I even tried registering a handler for UnhandledExceptions, but it didn't fire either.
我什至尝试为 UnhandledExceptions 注册一个处理程序,但它也没有触发。
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
The code that ultimately produces this, is:
最终产生这个的代码是:
return ConfigurationManager.ConnectionStrings[key].ConnectionString;, where ConnectionStrings[key]is null.
return ConfigurationManager.ConnectionStrings[key].ConnectionString;,这里ConnectionStrings[key]是null。
Update
更新
This is how applicaion errors are handled:
这是处理应用程序错误的方式:
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;
}
This is the attribute I use to decorate my base controller:
这是我用来装饰我的基本控制器的属性:
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;
}
}
And this is my customErrors:
这是我的customErrors:
<customErrors mode="On" />
As you can see these are pretty extensive, however they do not even fire in the case of accessing the ConnectionStringswhere the ConnectionStringdoesn't exist; which is kind of puzzling.
正如您所看到的,它们非常广泛,但是在访问不存在的ConnectionStrings地方时它们甚至不会触发ConnectionString;这有点令人费解。
It doesfire in any controller contained exception or exceptions not within a controller, so I don't understand why this case is any different.
它确实在任何包含异常的控制器中触发或不在控制器内的异常,所以我不明白为什么这种情况有什么不同。
回答by Darin Dimitrov
You haven't shown how exactly are you handling the errors in your Application_Error event nor how does your custom HandleAttribute is implemented so it's hard to guess what the problem might be. A common problem to watch for when implementing Application_Error is that you render some error view which itself throws an error. Think for example an error view that depends on a Layout and inside this layout you are calling a Html.Actionhelper to render the contents of another action that itself does database access and stuff. Your error views should be as static as possible. Ideally you should have a different Layout for them to avoid those kind of situations.
您还没有展示您是如何准确地处理 Application_Error 事件中的错误,也没有展示您的自定义 HandleAttribute 是如何实现的,因此很难猜测问题可能是什么。实现 Application_Error 时要注意的一个常见问题是您渲染了一些本身会引发错误的错误视图。例如,考虑一个依赖于布局的错误视图,在此布局中,您正在调用一个Html.Action助手来呈现另一个本身进行数据库访问等操作的内容。您的错误视图应尽可能保持静态。理想情况下,您应该为它们使用不同的布局以避免这种情况。
I may also suggest you taking a look at the following approachfor handling errors.
我还建议您查看以下处理错误的方法。
回答by jgauffin
Application_Errormight be the recommended way of handling errors in ASP.NET WebForms. But not in MVC.
Application_Error可能是处理 ASP.NET WebForms 中错误的推荐方法。但不是在 MVC 中。
We got error filters that take care of errors for us. The problem with the filter is that it only works when a controller has been invoked. Which is the problem for 404 and 401 errors (not found and authorization) and your database connection problem.
我们有错误过滤器可以为我们处理错误。过滤器的问题在于它仅在调用控制器时才起作用。这是 404 和 401 错误(未找到和授权)和您的数据库连接问题的问题。
customErrorsis the way to go here. I don't see why the redirect should be a problem?
customErrors是去这里的方式。我不明白为什么重定向应该是一个问题?
I'm going through proper error handling in a blog article: http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/
我正在博客文章中进行正确的错误处理:http: //blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/
回答by user3130351
To be correct to our visitors and the search engines, we should return meaningful HTTP status codes if errors occur, facing our site. It's not fair to return HTTP status code 200 on error, even if at the same time we return a view, explaining that an error occurred. if the user types in an incorrect address (the most frequent user fault), we should return HTTP status code 404 and not return or redirect to a View, where status code 200 will be returned.
There is a short and clean recommendations summary HERE.
为了对我们的访问者和搜索引擎正确,如果发生错误,我们应该返回有意义的 HTTP 状态代码,面对我们的网站。在错误时返回 HTTP 状态代码 200 是不公平的,即使我们同时返回一个视图,解释发生了错误。如果用户输入了错误的地址(最常见的用户错误),我们应该返回 HTTP 状态码 404,而不是返回或重定向到视图,视图将返回状态码 200。
有一个短暂的和干净的建议摘要这里。

