一、前言
编程中我们会使用特性(Attribute)标注到程序集、类、方法、属性上进行描述,在asp.net MVC或者Asp.net WebApi中使用过滤器(Filter)对Action、Result、Exception、Authorize进行AOP(切面编程)。并且过滤器和特性结合,将过滤器优雅的使用在类和属性上。本文针对特性与过滤器做一个总结。
二、定义
1、特性(Attribute),在微软官方文档的定义是使用特性,可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。按个人理解,就是对元数据的描述,类和方法,属性作为元数据,使用特性在元数据上使用特性,其作用就是对元数据补充说明,使用反射的方式获取特性内容,使用特性信息。
主要用途,比如描述类、方法和接口的 COM 属性、从标题、版本、说明或商标方面描述程序集、描述的方法的安全要求等详情参考msdn给的官方文档说明,总结就是基于元数据的描述信息,然后使用描述信息。
2、过滤器(Filter),提供了在asp.net MVC与asp.net webApi的请求处理管道过程中注入额外的逻辑,提供了一个简单而优雅的方式来实现横切关注点。主要分四类过滤器IAuthorizationFilter(授权过滤器)、IActionFilter(Action方法过滤器)、IResultFilter(ActionResult方法返回结果)、IExceptionFilter(异常过滤器),类库中通过继承接口提供了AuthorizeAttribute、ActionFilterAttribute、HandleErrorAttribute给开发者进行继承重写实现业务逻辑。
主要用途,比如基于AuthorizeAttribute(提供角色、用户名)方式的授权、日志信息、安全验证、图片防盗链、基于ActionFilterAttribute注入业务逻辑,请求和返回结果的处理等。
3、在过滤器中.NET FrameWork中提供两类,一类是在命名空间为using System.Web.Mvc下的提供给ASP.NET MVC的过滤器、一类是在命名空间using System.Web.Http.Filters下提供给ASP.NET WebApi的过滤器,两者不能混用,否则无法拦截生效。
三、使用
1、自定义的特性和过滤器项目
2、使用自定义特性在程序集、方法、属性等上,对其进行描述。通过反射的方式获取相应信息。
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace TQF.CustomFilterAttribute.App_Start.Filter { // _Attribute 接口运行时中 public class TestAttribute: Attribute { public string ActionName { get; set; } public TestAttribute() { } } }
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Web; using System.Web.Mvc; using TQF.CustomFilterAttribute.App_Start.Filter; namespace TQF.CustomFilterAttribute.Controllers { /// <summary> /// MVC请求 /// </summary> public class HomeController : Controller { public string Field { get; set; } public HomeController() { } //[TestMvcFilter(ActionName =nameof(Index))] [Test(ActionName =nameof(Index))] public ActionResult Index() { ViewBag.Title = "Home Page"; //获取类的属性描述 var classIfno = typeof(HomeController).GetCustomAttribute<TestAttribute>(); //获取指定属性的属性描述 var fieldIfno = typeof(HomeController).GetProperty("Field").GetCustomAttribute<TestAttribute>(); //获取指定方法的属性描述 var methodIfno = typeof(HomeController).GetMethod("Index").GetCustomAttribute<TestAttribute>(); return View(); } [HttpPost] public JsonResult Save() { //throw (new Exception("error", new InvalidCastException())); return new JsonResult(); } public ActionResult Error() { return View(); } } }
3、自定义AuthorizeAttribute过滤,包括ASP.NET MVC和ASP.NET WebApi
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TQF.CustomFilterAttribute.Models; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// Mvc请求权限授权过滤器 /// </summary> public class TestMvcAuthorizeAttribute: AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { var result = new ResultModel() { Message = "授权中" }; //actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, result); // 获取特殊设置的角色和用户,进行权限判断 if (Users == "admin") { // 返回,不进行权限验证或者验证成功 return; } else { } //base.OnAuthorization(filterContext); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web; using System.Web.Http; using System.Web.Http.Controllers; using TQF.CustomFilterAttribute.Models; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// webApi请求权限授权过滤器 /// </summary> public class TestHttpAuthorizeAttribute: AuthorizeAttribute { public override void OnAuthorization(HttpActionContext actionContext) { var result = new ResultModel() { Message = "授权中" }; //actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, result); // 获取特殊设置的角色和用户,进行权限判断 if (Users == "admin"){ // 返回,不进行权限验证或者验证成功 return; }else{ } base.OnAuthorization(actionContext); } } }
4、自定义ActionFilterAttribute过滤器(其包括实现了IActionFilter和IResultFilter接口)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// Mvc /// </summary> public class TestMvcActionFilterAttribute: ActionFilterAttribute { /// <summary> /// action 执行中 /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Response.Write("OnActionExecuting"); base.OnActionExecuting(filterContext); } /// <summary> /// action 执行结束 /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuted(ActionExecutedContext filterContext) { filterContext.HttpContext.Response.Write("OnActionExecuted"); base.OnActionExecuted(filterContext); } /// <summary> /// 生成结果中 /// </summary> /// <param name="filterContext"></param> public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.HttpContext.Response.Write("OnResultExecuting"); base.OnResultExecuting(filterContext); } /// <summary> /// /// </summary> /// <param name="filterContext"></param> public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.Write("OnResultExecuting"); base.OnResultExecuted(filterContext); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web; using System.Web.Http.Controllers; using System.Web.Http.Filters; using TQF.CustomFilterAttribute.Models; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// WebApi 请求的Action方法过滤器 /// </summary> public class TestHttpActionFilterAttribute: ActionFilterAttribute { /// <summary> /// 执行前 /// </summary> /// <param name="actionContext"></param> public override void OnActionExecuting(HttpActionContext actionContext) { var result = new ResultModel() { Message = "执行中"}; // 返回请求结果,不执行后续 actionContext.Response=actionContext.Request.CreateResponse(HttpStatusCode.OK, result); base.OnActionExecuting(actionContext); } /// <summary> /// 执行后 /// </summary> /// <param name="actionExecutedContext"></param> public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { var result = new ResultModel() { Message = "执行后" }; // 更改请求结果 actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.OK, result); base.OnActionExecuted(actionExecutedContext); } } }
5、自定义HandleErrorAttribute过滤器,对异常进行处理
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TQF.CustomFilterAttribute.Models; namespace TQF.CustomFilterAttribute.App_Start.Filter { /// <summary> /// 处理中产生的异常,异常过滤器 /// </summary> public class TestHandleErrorAttribute: HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { // 异常的具体信息 var exceptionType = filterContext.Exception.InnerException.GetType().FullName; var exceptionMessage = filterContext.Exception.InnerException.Message; var exceptionMethod = filterContext.Exception.TargetSite; var exceptionStackTrace = filterContext.Exception.StackTrace; // 写入日志文件 filterContext.ExceptionHandled = true; if (filterContext.HttpContext.Request.IsAjaxRequest()) { // 返回统一的错误信息 var result = new ResultModel(); filterContext.HttpContext.Response.ContentType = "application/json"; filterContext.HttpContext.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(result)); filterContext.HttpContext.Response.End(); } else { // 返回统一的错误页面 filterContext.HttpContext.Response.Redirect("/Home/Error"); base.OnException(filterContext); filterContext.HttpContext.Response.End(); } } } }
5、过滤器的使用,通过特性的方式将过滤器特性标注在Action上,或者在全局中进行注册过滤器,所以Action都使用。比如在Asp.NET MVC中提供的App_Start文件的FilterConfig中注册。
using System.Web; using System.Web.Mvc; using TQF.CustomFilterAttribute.App_Start.Filter; namespace TQF.CustomFilterAttribute { public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // filters.Add(new HandleErrorAttribute()); // filters.Add(new TestHandleErrorAttribute()); // filters.Add(new TestMvcActionFilterAttribute()); // filters.Add(new TestMvcAuthorizeAttribute()); // filters.Add(new TestFilter()); } } }
或者在Global.asax的Application_Start方法中注册。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using TQF.CustomFilterAttribute.App_Start.Filter; namespace TQF.CustomFilterAttribute { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { //AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); GlobalFilters.Filters.Add(new TestHttpActionFilterAttribute()); } } }
注意ASP.NET WebApi的fliter不能在FilterConfig类中注册,必须在WebApiConfig类中注册。
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; using TQF.CustomFilterAttribute.App_Start.Filter; namespace TQF.CustomFilterAttribute { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服务 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Filters.Add(new TestHttpActionFilterAttribute()); } } }
四、总结
1、过滤器提供AOP的编程方式,面向切面的关注点,动态注入业务逻辑,将相关的非主业务逻辑进行优雅解耦。使用特性的方式便利的使用在个Action上。
2、通过自定义过滤器,重写过滤器的虚方法,使用方法提供的参数上下文信息(请求数据的请求体、请求头、请求结果、异常信息)处理业务逻辑。
3、各过滤的执行顺序依次是IAuthorizationFilter->IActionFilter->IResultFilter->IExceptionFilter,首先授权的验证,Action方法的处理和返回结果、产生的异常信息。