.NET MVC全局异常处理(二)
对上节的内容进行了补充
MVC过滤器Filter
MVC有四种过滤器:Authorization、Exception、Action、Result,我们要用的的就是Exception异常过滤器
当我们新建一个MVC项目后,异常过滤器就已经自动在程序中注册了,先从上一节所说的全局配置文件开始,Global.asax这个文件中的Application_Start方法会在程序启动时运行,其中的即默认注册了全局过滤器,如图
我们可以进入RegisterGlobalFilters方法查看,这个方法中默认注册了一个异常处理过滤器,也就是说默认状态的MVC程序发生异常时会被程序捕获处理,处理方式是跳转至错误页面,也就是上一篇文章说的Layout文件夹下面的Error页
但使用异常过滤器有一个大前提是要在Web.config中打开自定义错误处理的设置,customErrors节点要设置为“On”,这一设置默认是关闭的,也就是说要手动加上才行
<system.web>
<compilation debug="true" targetFramework="4.6.1"/>
<httpRuntime targetFramework="4.6.1"/>
<customErrors mode="On">
</customErrors>
</system.web>
需要注意的是 404 错误,这种类型的异常并不会被过滤器捕获
但是可以在web.config中添加节点进行自定义配置,跳转到相应的页面
<customErrors mode="On">
<error redirect="~/Error/NotFound" statusCode="404" />
</customErrors>
public class ErrorController : Controller
{
// GET: Error
public ActionResult Index()
{
return View();
}
public ActionResult CustomHttpError()
{
ViewBag.Message = "有错误";
//HandleErrorInfo
//ExceptionContext
return View();
}
public ActionResult NotFound()
{
return View();
}
}
自定义过滤器
MVC默认的异常过滤器可以满足基本的需要,但是如果要对一些异常进行特殊处理就需要我们自定义过滤器的内容,可以通过重写OnException方法达到这个目的
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
/* 调用基类的OnException方法,实现基础的功能。
* 如果要完全的自定义,就不需要调用基类的方法
*/
base.OnException(filterContext);
/* 此处可进行记录错误日志,发送错误通知等操作
* 通过Exception对象和HttpException对象可获取相关异常信息。
* Exception exception = filterContext.Exception;
* HttpException httpException = new HttpException(null, exception);
*/
}
}
示例代码
public class MyErrorHandler : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
return;
var statusCode = (int) HttpStatusCode.InternalServerError;
if (filterContext.Exception is HttpException)
{
statusCode = filterContext.Exception.As<HttpException>().GetHttpCode();
}
else if (filterContext.Exception is UnauthorizedAccessException)
{
//to prevent login prompt in IIS
// which will appear when returning 401.
statusCode = (int)HttpStatusCode.Forbidden;
}
_logger.Error("Uncaught exception", filterContext.Exception);
var result = CreateActionResult(filterContext, statusCode);
filterContext.Result = result;
// Prepare the response code.
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = statusCode;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
{
var ctx = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
var statusCodeName = ((HttpStatusCode) statusCode).ToString();
var viewName = SelectFirstView(ctx,
"~/Views/Error/{0}.cshtml".FormatWith(statusCodeName),
"~/Views/Error/General.cshtml",
statusCodeName,
"Error");
var controllerName = (string) filterContext.RouteData.Values["controller"];
var actionName = (string) filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
var result = new ViewResult
{
ViewName = viewName,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
};
result.ViewBag.StatusCode = statusCode;
return result;
}
protected string SelectFirstView(ControllerContext ctx, params string[] viewNames)
{
return viewNames.First(view => ViewExists(ctx, view));
}
protected bool ViewExists(ControllerContext ctx, string name)
{
var result = ViewEngines.Engines.FindView(ctx, name, null);
return result.View != null;
}
}