异常处理是所有应用程序的的一个重要部分。ASP.NET 为处理异常提供了数种不同的方法。在这篇文章中,我们将学习如何最佳的实现 ASP.NET MVC 异常处理。
MVC异常处理的5种方法
在 .NET 、ASP.NET 和 MVC 之间有几种可行的方式来处理应用程序异常。
- Web.Config 文件中的 <customErrors> 结点
- MVC 的 HandleError 特性
- Controller.OnException 方法
- HttpApplication Application_Error 事件
- 使用Stackify 的 Retrace 收集异常
这些异常处理方法都有各自的优缺点。您可能需要组合使用它们才能正确处理异常并记录错误信息。
在异常处理时,你需要完成两个重要的事情:
- 优雅地处理异常并向用户展示友好的错误页面。
- 记录错误方便您了解并监控错误信息。
必须项:在 Web.Config 的 <customErrors> 结点中配置全局错误页面
向您的用户展示“yellow screen of death(黄色错误页)”是您的最后一个选择。如果您不知道那是什么的话,那正是我所谈论的标准的黄色ASP.NET错误页面。
对于所有应用程序,我总是建议在您的 Web.Config 中指定自定义错误页面。最坏情景下,如果出现未处理的异常,用户看到的也将是配置中指定的自定义错误页面。
<system.web>
<customErrors mode="On" defaultRedirect="~/ErrorHandler/Index">
<error statusCode="404" redirect="~/ErrorHandler/NotFound"/>
</customErrors>
<system.web/>
更多信息:How to Use Web.Config customErrors for ASP.NET
使用 MVC HandleErrorAttribute 自定义 Responses
HandleErrorAttribute 继承自 FilterAttribute ,可以应用于整个 controller 或着 controller 中某个的 action 方法。
---------------
/*
* HandleErrorAttribute 定义如下:
*/
public class HandleErrorAttribute : FilterAttribute, IExceptionFilter
{
//Other members...
/// <summary>Called when an exception occurs.</summary>
/// <param name="filterContext">The action-filter context.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="filterContext" /> parameter is null.</exception>
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (filterContext.IsChildAction || filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
return;
Exception exception = filterContext.Exception;
//500
if (new HttpException((string)null, exception).GetHttpCode() != 500 || !this.ExceptionType.IsInstanceOfType((object)exception))
return;
string controllerName = (string)filterContext.RouteData.Values["controller"];
string actionName = (string)filterContext.RouteData.Values["action"];
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
ExceptionContext exceptionContext = filterContext;
ViewResult viewResult1 = new ViewResult();
viewResult1.ViewName = this.View;
viewResult1.MasterName = this.Master;
viewResult1.ViewData = (ViewDataDictionary)new ViewDataDictionary<HandleErrorInfo>(model);
viewResult1.TempData = filterContext.Controller.TempData;
ViewResult viewResult2 = viewResult1;
exceptionContext.Result = (ActionResult)viewResult2;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
HandleErrorAttribute 只能处理在MVC action方法中发生的 Http Status Code 等于 500(Internal Server Error)的错误。它不会追踪在MVC管道之外帮助的异常。在其他HTTP模块、MVC路由等可能出现异常。
什么时候该使用 HandleErrorAttribute ?
由于它无法捕获所有的可能出现异常,所以对于全局未处理的异常处理来说这是一个糟糕的解决方案。
它适用于为特定的 MVC controller 或 action 方法指定特定的错误页面。在您的 Web.Config 的 <customErrors> 结点中,指定一个错误页面适用于通用(全局)错误页面。HandleErrorAttribute 可以给你更精细的控制,如果您需要的话。
注意:如果您需要使用 HandleErrorAttribute,请确保在您的Web.Config 中 <customErrors> 结点设为 On。
举个例子,当出现 SqlException 异常时,你希望显示一个特定的页面,你可以使用类似于如下的代码:
[HandleError(ExceptionType = typeof(SqlException), View = "SqlExceptionView")]
public string GetClientInfo(string username)
{
return Dll.Query(username);
}
使用 HandleErrorAttribute 的缺点是,它无法记录异常日志!
使用 MVC Controller OnException 来自定义 Responses
OnException 方法与 HandleErrorAttribute 相似,但是更为灵活。它可以处理所有的 Http Status Code,而不仅仅是 Http Status Code 等于 500 的错误。同样你也可以记录异常日志。
/*
* IExceptionFilter 接口 的 OnException 方法定义如下:
*/
namespace System.Web.Mvc
{
/// <summary>Defines the methods that are required for an exception filter.</summary>
public interface IExceptionFilter
{
/// <summary>Called when an exception occurs.</summary>
/// <param name="filterContext">The filter context.</param>
void OnException(ExceptionContext filterContext);
}
}
示例:
public class UserMvcController : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
//Log the error!!
_Logger.Error(filterContext.Exception);
//Redirect or return a view, but not both.
filterContext.Result = RedirectToAction("Index", "ErrorHandler");
// OR
filterContext.Result = new ViewResult
{
ViewName = "~/Views/ErrorHandler/Index.cshtml"
};
}
}
什么时候使用 OnException 方法处理 MVC 异常?
如果您希望为用户展示自定义的错误页面,或者记录自定义异常信息,那 OnException 就是一个很棒的解决方案。 相比 HandleErrorAttribute 它更为灵活,而且不需要将 Web.Config 中的 <customErrors> 设置为 On。
注意: 所有 HTTP status codes 都会调用 OnException 方法。所以对于简单错误(如:由于错误的Url引发的404错误)的处理需要你十分小心。
使用 HttpApplication Application_Error 作为全局异常处理
到目前为止,我们已经介绍了三种不同的方法来自定义异常发生时您的用户将看到的响应,OnException 是你唯一可以用来记录异常信息的选项。
为了记录应用程序中可能发生的所有未处理的异常,你应该实现基本错误的日志记录功能,如下所示:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
protected void Application_Error()
{
var ex = Server.GetLastError();
//log the error!
_Logger.Error(ex);
}
}
什么时候使用 Application_Error ?
Always!HttpApplication 的 Application_Error 方法提供了收集和记录所有未处理的错误的最佳机制。
用Stackify Retrace收集所有.NET异常
Stackify的APM解决方案Retrace轻松访问.NET性能分析API,以跟踪应用程序的性能,直至代码级别。 作为它的一部分,它可以自动收集所有未处理的异常,或被配置为接收所有抛出的异常,即使它们被处理和丢弃。 Retrace不需要任何代码更改!
Retrace允许您查看和监视所有的应用程序错误。 查看我们的错误监控功能以了解更多信息。
总结
有多种方法可以完成 MVC 错误处理。您总是应该在 Web.Config中的 <customErrors>结点中指定默认的错误页面,并且在 HttpApplication 的 Application_Error 方法中记录未处理的异常信息。
HandleErrorAttribute 或者 OnException 为用户显示错误方式提供更为精细的控制。
如果你想跟踪所有的应用程序异常,请确保检查我们的Retrace和我们的错误监控功能。 您还可以使用我们的免费探查器Prefix,免费查看您的工作站上的所有应用程序异常。