在ASP.NET MVC中,默认的异常处理机制有时候没法知足项目的业务需求,咱们能够经过实现IExceptionFilter
接口编写本身想要的异常处理代码,好比全局异常捕获,记录错误日志等自定义异常处理操做。html
文章演示代码的下载地址:GlobalExceptionHandle-By-IExceptionFilter ,项目使用的是VS2013开发工具,ASP.NET MVC 5框架,建议下载查看演示,有更加详细的代码和直观的界面。web
ASP.NET MVC默认提供了HandleError
特性做为全局异常处理的机制,可是有部分的局限性,好比不捕捉404错误,依赖ASP.NET的自定义错误模块,对SEO不友好(由于错误页面跳转的方式为302)等等,因此若是想在项目中对异常错误进行更多自定义操做,能够继承并重写HandleError
特性,或者使用IExceptionFilter
编写本身的异常处理特性。编程
PS:HandleError
也是经过实现IExceptionFilter
接口来进行相关操做,具体能够查看HandleErrorAttribute
源码。若是没用到HandleError
特性的一些属性,仍是推荐使用IExceptionFilter
接口,这样代码会比较精炼,而且能够更自由的进行功能扩展和代码编写。swift
这里先新建一个ASP.NET MVC项目,接着新建一个类库,之后全部自定义特性和过滤器的类都放在这个类库下,这里我将类库命名为CustomAttributeClassLibrary。服务器
接下来在类库中添加要编写自定义异常处理的类,这里我命名为CustomExceptionAttribute。mvc
CustomExceptionAttribute类代码以下:框架
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; namespace CustomAttributeClassLibrary { public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter { public void OnException(ExceptionContext filterContext) { Exception exception = filterContext.Exception; if (filterContext.ExceptionHandled == true) { return; } HttpException httpException = new HttpException(null, exception); //filterContext.Exception.Message可获取错误信息 /* * 一、根据对应的HTTP错误码跳转到错误页面 * 二、先对Action方法里引起的HTTP 404/400错误进行捕捉和处理 * 三、其余错误默认为HTTP 500服务器错误 */ if (httpException != null && (httpException.GetHttpCode() == 400 || httpException.GetHttpCode() == 404)) { filterContext.HttpContext.Response.StatusCode = 404; filterContext.HttpContext.Response.WriteFile("~/HttpError/404.html"); } else { filterContext.HttpContext.Response.StatusCode = 500; filterContext.HttpContext.Response.WriteFile("~/HttpError/500.html"); } /*--------------------------------------------------------- * 这里可进行相关自定义业务处理,好比日志记录等 ---------------------------------------------------------*/ //设置异常已经处理,不然会被其余异常过滤器覆盖 filterContext.ExceptionHandled = true; //在派生类中重写时,获取或设置一个值,该值指定是否禁用IIS自定义错误。 filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; } } }
上面代码中主要实现了一个IExceptionFilter
接口和一个FilterAttribute
基类。这里先说IExceptionFilter
接口,接口只提供了一个方法OnException
,主要的参数为ExceptionContext
类,基本上就是经过ExceptionContext
类来获取相关错误信息,以及进行对应的HTTP请求和响应操做。工具
ExceptionContext类文档的截图说明:post
至于FilterAttribute
抽象类,若是打算将编写的异常过滤器做为特性来使用,那么它必须继承自FilterAttribute
或者它的子类HandleErrorAttribute
。不做为特性使用的全局操做过滤器能够不继承这个基类。学习
下图演示能够看到没有继承FilterAttribute
类就没法做为特性来使用:
可是不影响全局操做过滤器的注册和使用:
记得使用NUGET将ASP.NET MVC 5 安装到新类库中,IExceptionFilter
是属于System.Web.Mvc
程序集,并且编写自定义的过滤器和特性会使用到不少与System.Web.Mvc
命名空间有关的类。
到这里就完成了整个自定义异常处理过滤器的功能。
有两种使用方式,一种是经过全局过滤器类GlobalFilterCollection
进行注册,还有一种是单独的做为特性在控制器或操做方法中使用。
咱们先将自定义特性类库引用到默认的MVC项目中:
全局注册的使用只须要在FilterConfig
过滤器配置类中注册下便可,代码以下:
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute(), 1); filters.Add(new CustomExceptionAttribute(), 2); } }
PS:关于FilterConfig
类,若是不清楚的朋友能够看这篇文章:ASP.NET MVC 5 学习笔记之FilterConfig类
这里要注意筛选器的运行顺序设置,引用文档资料:
在 ASP.NET MVC 版本 1 和 2 中,OnException(ExceptionContext) 筛选器以正向顺序运行。 在 ASP.NET MVC 版本 3 及更高版本中,此顺序已反转。
因此Order=2的优先级反而比较高,注册好后整个MVC项目全部的异常都会被咱们的CustomExceptionAttribute类捕捉和处理。
单独的做为特性使用有一个前提,就是筛选器须要继承FilterAttribute
基类,上面演示代码中已有说明,使用方法其余的特性同样,只须要在控制器或者相应的操做方法上方声明便可:
[CustomException] public ActionResult Index() { return View(); }
这里贴几段引起异常的操做方法代码,把下面代码放到演示项目中默认的Home控制器中:
/// <summary> /// 抛出HTTP 500 /// </summary> /// <returns></returns> public ActionResult ThrowHttp500() { throw new HttpException(500, "服务器错误"); } /// <summary> /// 抛出HTTP 404 /// </summary> /// <returns></returns> public ActionResult ThrowHttp404() { throw new HttpException(404, "页面未找到"); } /// <summary> /// 抛出未引用对象异常 /// </summary> /// <returns></returns> public ActionResult ThrowNullReferenceException() { throw new NullReferenceException(); } /// <summary> /// 引起输入字符串的格式不正确异常 /// </summary> /// <returns></returns> public ActionResult ThrowFormatException() { string str = ""; int count = Convert.ToInt32(str); return View("Index"); }
网站生成运行后输入路径测试,结果以下图:
这里总结使用IExceptionFilter
编写自定义异常过滤器的须要注意的地方:
1、并不依赖自定义异常错误,因此web.config配置文件中的customErrors mode="On"
或者mode="Off"
都没法影响到咱们本身编写的过滤器的使用。
可是若是你想要使用customErrors来进行控制,那么也很简单,在咱们自定义的过滤器中,只须要使用filterContext.HttpContext.IsCustomErrorEnabled
属性进行判断便可:
2、只能处理Action操做方法里抛出的异常,超出此范围的异常是没法被捕捉到的。这点很是重要,这是ASP.NET MVC中筛选器自身的特性。不管你是在Controller上声明筛选器,或者经过全局注册,最终都只能应用到某个具体的Action方法。这里所谓的全局注册,其实就是指全部的Action都应用了该筛选器。一样的,若是筛选器在控制器上声明,其实也是表示该控制器下全部的操做方法都要应用此筛选器。筛选器自己只针对ACtion方法!在Controller上声明和全局注册只是设定了其应用范围!
这里引用《ASP.NET MVC高级编程》的说明:
异常过滤器用来处理操做或结果执行期间可能抛出的异常。
注意:IExceptionFilter
也能捕获Action方法的其余筛选器所抛出的异常。例如实现IAuthorizationFilter
,IActionFilter
或者IResultFilter
的筛选器若是执行期间抛出异常,都是能被捕捉到的。
PS:筛选器=过滤器!不一样的翻译别称。
主要没法处理的范围和状况有:
若是遇到上面的状况又想捕获到异常,也是有两个解决办法的,一个是使用Global.asax的Application_Error事件处理这部分筛选器没法捕捉的异常。另一个是直接设置IIS上的.NET错误便可,其实就是设置自定义异常错误,设置web.config配置文件中的customErrors
节点的mode="On"
,而后设置对应的error
节点和相应的HTTP错误跳转页面。注意自定义筛选器的优先级顺序要设置下,若是没有什么业务需求而且不考虑SEO就可使用这种简单的方法。
3、IIS级的错误并不会被捕捉到,由于此类错误还未进入ASP.NET 的处理流程,这个问题上面第二点就有提到。好比下图中输入某个物理路径不存在的页面,这里直接就显示IIS默认的错误页,须要在服务器上进行相关设置:
4、在咱们的筛选器CustomExceptionAttribute代码中,能够看到使用了一个ExceptionHandled
属性:
if (filterContext.ExceptionHandled == true)
这个属性十分重要,是为了防止使用多个异常过滤器致使操做发生冲突。在使用多个异常过滤器的状况下此属性做用十分明显!
若是为true,则表示已有其余filter处理过此异常 ,就不须要再次进行处理了,以避免覆盖其余filter所解决的Exception。
若是为false,就要对该Exception进行相应的处理,处理以后将ExceptionHandled
属性设置为true,表示异常已经处理,不然操做会被其余异常过滤器覆盖掉,形成冲突。
默认状况下若是ExceptionHandled
属性没有被其余异常处理过滤器设置为true ,那么ASP.NET MVC将使用默认的异常处理,若是没有开启customErrors
,将会显示异常详细页面(错误黄页)。
5、操做相关的页面跳转,若是网站要考虑搜索引擎优化方法,必定不要使用下面的两种方法进行跳转:
//filterContext.HttpContext.Response.Redirect("~/HttpError/500.html"); //filterContext.HttpContext.Server.Transfer("~/HttpError/500.html");
由于这两种方法你设置了StatusCode是无效的。
此外要注意路由在传统的ASP.NET 跳转方法中存在不兼容问题,若是想要使用MVC的路由和视图进行跳转,能够参考下面的代码:
/*-------------------------------------------------------------------------- * 下面的范例是使用MVC控制器和视图进行跳转,具体可查阅HandleErrorAttribute源码 --------------------------------------------------------------------------*/ string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); filterContext.Result = new ViewResult { ViewName = "视图名称", MasterName = "母板名称", ViewData = new ViewDataDictionary<HandleErrorInfo>(model), TempData = filterContext.Controller.TempData }; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.Clear(); filterContext.HttpContext.Response.StatusCode = 500;
上面的代码在视图结果中还传入了错误信息实体,若是只想要简单的跳转到某个视图或控制器,其实只要设置ActionResult
便可,代码以下:
filterContext.Result = new ViewResult() { ViewName="视图名称"}; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.Clear(); filterContext.HttpContext.Response.StatusCode = 500;
出处:https://shiyousan.com/post/635833789557065314
版权声明:本文采用知识共享许可协议:署名-相同方式共享 4.0 国际(CC BY-SA 4.0)。欢迎转载本文,转载请声明出处或保留此段声明。