Filters inject extra logic into the request processing pipeline.
They provide a simple and elegant way to implement cross-cutting concerns.
英文资料的开篇给出了Filter的定义:1.在请求过程中注入额外的逻辑 2.用优雅的方式分离关注点
根据时间点分类:
Authorization --> Action --> Result , Exception
IAuthorizationFilter:在所有的Filter中最早执行的,可用于Action的权限控制
IActionFilter:在Action执行的前后,可用于请求跳转、记录Action执行日志
IResultFilter:在Result执行的前后,可用于记录Result执行日志
IExceptionFilter:在任何异常发生时,可用于记录日志、处理异常、指定错误页面
如何使用:
// 1.在Global.asax中定义,应用在所有的Action上
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
// 2.在Controller上定义,应用在包含的Action上
[CustomAuthorize]
public class BaseController : Controller
{}
// 3.在Action上定义,应用在该Action上
[ValidateAntiForgeryToken]
[HttpPost]
public virtual ActionResult PostAction(string input)
{
string output = "You have input: " + input;
return RedirectToAction(MVC.AvoidDuplicateSubmit.GetAction(output));
}
同一类型的Filter执行时有先后顺序:Global > Controller > Action ,
只有IExceptionFilter的顺序是倒过来的
自定义Filter:
// 1.自定义授权Filter
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// 不对ChildAction进行权限检查
if (filterContext.IsChildAction)
{
return;
}
base.OnAuthorization(filterContext);
}
/// <summary>
/// 授权逻辑
/// </summary>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string controllerName = httpContext.Request.RequestContext.RouteData.Values["controller"].ToString();
string actionName = httpContext.Request.RequestContext.RouteData.Values["action"].ToString();
string action = string.Format("/{0}/{1}", controllerName, actionName);
// 允许的Role:action --> Activity --> Roles
base.Roles = SysContext.GetRolesForActivity(action);
// 用户的Role:从Session或Cookie中取得(在哪存的到哪取)
string userRole = "Guest";
httpContext.User = new GenericPrincipal(httpContext.User.Identity, new string[] { userRole });
return base.AuthorizeCore(httpContext);
}
/// <summary>
/// 无权限处理
/// </summary>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
// Ajax须返回JsonResult
filterContext.Result = new JsonResult
{
Data = new { Error = "未授权", LogOnUrl = FormsAuthentication.LoginUrl },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
// 默认显示登录页面,也可以自定义
filterContext.Result = new ViewResult { ViewName = MVC.Shared.Views.Error };
// base.HandleUnauthorizedRequest(filterContext);
}
}
}
// 2.自定义ActionFilter
public class CustomActionAttribute : FilterAttribute, IActionFilter
{
private Stopwatch timer;
public void OnActionExecuting(ActionExecutingContext filterContext)
{
timer = Stopwatch.StartNew();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
timer.Stop();
string action = string.Format("/{0}/{1}",
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
filterContext.ActionDescriptor.ActionName);
string exacuteTimeLog = string.Format("\r\nAction: {0} ElapsedTime: {1}ms \r\n", action, timer.Elapsed.TotalMilliseconds);
SysContext.ActionPerformanceLogger.Debug(exacuteTimeLog);
}
}
// 3.自定义ResultFilter
public class CustomResultAttribute : FilterAttribute, IResultFilter
{
private Stopwatch timer;
public void OnResultExecuting(ResultExecutingContext filterContext)
{
timer = Stopwatch.StartNew();
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
timer.Stop();
string view = (((System.Web.Mvc.ViewResultBase)(filterContext.Result))).ViewName;
string exacuteTimeLog = string.Format("\r\nView: {0} ElapsedTime: {1}ms \r\n", view, timer.Elapsed.TotalMilliseconds);
SysContext.ActionPerformanceLogger.Debug(exacuteTimeLog);
}
}
// 4.自定义异常处理Filter
public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
// 1.记录异常日志
// TODO
// 2.实施补救措施
// TODO
// 3.展示错误页面:默认为~/Views/Shared/Error.cshtml,由HandleErrorAttribute捕获
if (ex is NullReferenceException)
{
filterContext.Result = new ViewResult { ViewName = MVC.Home.Views.Index };
}
else if (ex is HttpAntiForgeryException)
{
filterContext.Result = new RedirectResult("/Authentication/LogOn");
}
// ...
}
}
MVC3内置的Filter:
IAuthorizationFilter:RequireHttps,ChildActionOnly,ValidationAntiForgeryToken,ValidateInput
IActionFilter:OutputCache,AsyncTimeout,NoAsyncTimeout
IExceptionFilter: HandleError