ASP.NET MVC IActionFilter IResultFilter IExceptionFilter/HandleError
一、IActionFilter
1、基本定义
在action的执行前后进行AOP拦截。
IActionFilter接口定义如下:
public interface IActionFilter {
//执行方法之前 void OnActionExecuted(ActionExecutedContext filterContext); //----> ActionInvoke(xxxx) <-----执行Actin方法
//执行方法之后
void OnActionExecuting(ActionExecutingContext filterContext); }
2、实现方式
(1)、控制器重写IActionFilter实现方法
控制器已经实现了接口IActionFilter,每次请求都会创建一个Controller实例,而且只执行一个Action方法,所以可以在控制器层次进行拦截,这样重载了的控制器的每个Action都会被拦截,Controller的定义如下:
public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable,IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
所以,我们只需要重写已经实现的IActionFilter的两个方法,如下:
protected override void OnActionExecuting(ActionExecutingContext filterContext) { var contrllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; var actionName = filterContext.ActionDescriptor.ActionName; var parameter = filterContext.ActionDescriptor.GetParameters(); filterContext.HttpContext.Response.Write(contrllerName + "_" + actionName); base.OnActionExecuting(filterContext); } protected override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); }
(2)、定义一个FilterAttribute
定义Attribute放到不同的Action
public class MyCustomAttribute : FilterAttribute, IActionFilter { public void OnActionExecuted(ActionExecutedContext filterContext) { } public void OnActionExecuting(ActionExecutingContext filterContext) { //1.如果result不为空,那么就return了。不执行递归。。。 filterContext.Result = new ViewResult() { ViewName = "Error" }; } }
(3)、Global全局注入
每一个action上面加特性,比较麻烦,我们可以选择进行全局的注入。
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new MyCustomAttribute()); }
二、IResultFilter
1、基本概念
Action的返回值有几下几种,他们都是继承自ActionResult,其主要作用是对Action的执行结果进行转换,如利用内容协商,把结果数据序列化为指定格式的数据。
ActionView、 ViewResult、JsonResult、ContentResult
ActionResult定义如下:
public abstract class ActionResult { public abstract void ExecuteResult(ControllerContext context); }
在ActionResult的执行(调用ExecuteResult方法)前后进行AOP拦截。
接口定义如下:
public interface IResultFilter { void OnResultExecuted(ResultExecutedContext filterContext); void OnResultExecuting(ResultExecutingContext filterContext); }
2、实现方式
同IActionFilter一样,有三种方式
(1)、Controller上Override的
public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable,IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
public class HomeController : Controller { protected override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); } protected override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); } }
(2)、继承FilterAttribute,IResultFilter或ActionFilterAtrribute
public class MyActionFilterAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); }
(3)、GlobleFilters 全局的注册
同IActionFilter
三、 IExceptionFilter HandleError
1、基本概念
IExceptionFilter定义如下
public interface IExceptionFilter { void OnException(ExceptionContext filterContext); }
public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable,IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
Controller抽象类,接口IExceptionFilter的实现,空的
protected virtual void OnException(ExceptionContext filterContext)
{
}
HandleError是IExceptionFilter在MVC中的默认实现,也是默认注册在全局
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } }
HandleError当出现异常时候,转到一个错误页面,其源代码如下:
public virtual void OnException(ExceptionContext filterContext) { if (filterContext == null) throw new ArgumentNullException(nameof (filterContext)); if (filterContext.IsChildAction || filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) return; Exception exception = filterContext.Exception; 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,跳转到其对应View页面 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;
//ExceptionHandled标记已经处理 filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.Clear();
//定义500错误 filterContext.HttpContext.Response.StatusCode = 500; filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; }
public string View { get {
//view为空就是Error页面,Share文件夹下的Error.cshtml if (string.IsNullOrEmpty(this._view)) return "Error"; return this._view; } set { this._view = value; } }
2、扩展例子
不仅要错误页面,还要记录日志到数据库
public class MyHandleErrorAttribute : FilterAttribute, IExceptionFilter// HandleErrorAttribute { public void OnException(ExceptionContext filterContext) { //记录日志。。。。my logic filterContext.ExceptionHandled = true;
//这个要设置为true,要不会继续抛出异常
base.OnException(filterContext); } }
public class HomeController : Controller { [MyHandleError] public ActionResult Index() { throw new Exception(""); return View(); } }
配置Web.config
<configuration>
<system.web>
<customErrors mode="On"/>
</system.web>
</configuration>
可以直接跳到错误页面,要不直接显示红色的错误,黄色页。