一, 操作过滤器
1, 定义
操作过滤器用于实现 IActionFilter 接口以及包装操作方法执行. IActionFilter 接口声明两个方法: OnActionExecuting 和 OnActionExecuted.OnActionExecuting 在操作方法之前运行. OnActionExecuted 在操作方法之后运行, 可以执行其他处理, 如向操作方法提供额外数据, 检查返回值或取消执行操作方法.
查看 ActionFilterAttribute 类的定义:
- #region 程序集 System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
- // D:\Practice\MVC \ 自定义操作过滤器 \ MVCCustomerActionFilterDemo\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll
- #endregion
- namespace System.Web.Mvc
- {
- //
- // 摘要:
- // 表示筛选器特性的基类.
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
- public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
- {
- //
- // 摘要:
- // 初始化 System.Web.Mvc.ActionFilterAttribute 类的新实例.
- protected ActionFilterAttribute();
- //
- // 摘要:
- // 在执行操作方法后由 ASP.NET MVC 框架调用.
- //
- // 参数:
- // filterContext:
- // 筛选器上下文.
- public virtual void OnActionExecuted(ActionExecutedContext filterContext);
- //
- // 摘要:
- // 在执行操作方法之前由 ASP.NET MVC 框架调用.
- //
- // 参数:
- // filterContext:
- // 筛选器上下文.
- public virtual void OnActionExecuting(ActionExecutingContext filterContext);
- //
- // 摘要:
- // 在执行操作结果后由 ASP.NET MVC 框架调用.
- //
- // 参数:
- // filterContext:
- // 筛选器上下文.
- public virtual void OnResultExecuted(ResultExecutedContext filterContext);
- //
- // 摘要:
- // 在执行操作结果之前由 ASP.NET MVC 框架调用.
- //
- // 参数:
- // filterContext:
- // 筛选器上下文.
- public virtual void OnResultExecuting(ResultExecutingContext filterContext);
- }
- }
根据方法的名字就知道 4 个方法执行的顺序了:
OnActionExecuting 是 Action 执行前的操作, OnActionExecuted 则是 Action 执行后的操作, OnResultExecuting 是解析 ActionResult 前执行, OnResultExecuted 是解析 ActionResult 后执行.
即: Action 执行前: OnActionExecuting 方法先执行→Action 执行 →OnActionExecuted 方法执行→OnResultExecuting 方法执行→返回的 ActionRsult 中的 executeResult 方法执行→OnResultExecuted 执行.
2, 案例
2.1, 创建自定义操作过滤器
新建一个自定义过滤器, 然后重新里面的方法, 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- namespace MVCCustomerActionFilterDemo.Extension
- {
- public class CustomerActionFilter :ActionFilterAttribute
- {
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法准备执行");
- base.OnActionExecuting(filterContext);
- }
- public override void OnActionExecuted(ActionExecutedContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法执行结束");
- base.OnActionExecuted(filterContext);
- }
- }
- }
2.2, 新建控制器
创建一个控制器, 用来测试自定义操作过滤器, 代码如下:
- using MVCCustomerActionFilterDemo.Extension;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- namespace MVCCustomerActionFilterDemo.Controllers
- {
- public class ActionFiltersController : Controller
- {
- // GET: ActionFilters
- [CustomerActionFilter]
- public ActionResult Index()
- {
- Response.Write("<h2 > 执行 Index...</h2>");
- return View();
- }
- }
- }
Index 方法对应的视图代码如下:
- @{
- Layout = null;
- }
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <meta name="viewport" content="width=device-width" />
- <title>
- Index
- </title>
- </head>
- <body>
- <div>
- <h1>
- 操作过滤器测试页面
- </h1>
- </div>
- </body>
- </HTML>
运行结果;
二, 结果过滤器
1, 定义
结果筛选器用于实现 IResultFilter 接口以及包装 ActionResult 对象的执行. IResultFilter 接口声明两个方法 OnResultExecuting 和 OnResultExecuted.OnResultExecuting 在执行 ActionResult 对象之前运行. OnResultExecuted 在结果之后运行, 可以对结果执行其他处理, 如修改 HTTP 响应.
结果过滤器也是实现了 ActionFilterAttribute 类.
2, 案例
修改 CustomerActionFilter 类, 重写 OnResultExecuting 和 OnResultExecuted, 修改后的代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- namespace MVCCustomerActionFilterDemo.Extension
- {
- public class CustomerActionFilter :ActionFilterAttribute
- {
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法准备执行");
- base.OnActionExecuting(filterContext);
- }
- public override void OnActionExecuted(ActionExecutedContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法执行结束");
- base.OnActionExecuted(filterContext);
- }
- public override void OnResultExecuting(ResultExecutingContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法执行结束, 准备呈现视图");
- base.OnResultExecuting(filterContext);
- }
- public override void OnResultExecuted(ResultExecutedContext filterContext)
- {
- filterContext.HttpContext.Response.Write("视图呈现结束");
- base.OnResultExecuted(filterContext);
- }
- }
- }
运行结果:
三, 案例
1, 记录操作
在真实项目中, 可以利用操作过滤器记录哪个用户登录系统以后进行了哪些操作.
1.1, 创建实体类
新建用于记录信息的实体类. 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- namespace MVCCustomerActionFilterDemo.Models
- {
- public class LogEntity
- {
- /// <summary>
- /// 控制器名称
- /// </summary>
- public string ControllerName { get; set; }
- /// <summary>
- /// Action 方法名称
- /// </summary>
- public string ActionName { get; set; }
- /// <summary>
- /// 操作用户 id
- /// </summary>
- public string OperationUserId { get; set; }
- /// <summary>
- /// 操作时间
- /// </summary>
- public DateTime OperationTime { get; set; }
- }
- }
1.2, 创建日志类
创建日志帮助类, 代码如下:
- using MVCCustomerActionFilterDemo.Models;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.IO;
- namespace MVCCustomerActionFilterDemo.Util
- {
- public class LogHelper
- {
- /// <summary>
- /// 记录操作日志
- /// 这里为了方便测试记录到 txt 文件里面, 实际中应该是记录到数据库中
- /// 然后有界面可以显示这些操作记录
- /// </summary>
- /// <param name="entity"></param>
- public static void WriteOperRecore(LogEntity entity)
- {
- string strPath = @"C:\log.txt";
- using (StreamWriter sw = new StreamWriter(strPath, true))
- {
- sw.WriteLine("**************************");
- sw.WriteLine($"操作时间:{entity.OperationTime}");
- sw.WriteLine($"当前 Controller 名称:{entity.ControllerName}");
- sw.WriteLine($"当前 Action 名称:{entity.ActionName}");
- sw.WriteLine($"当前操作用户 id:{entity.OperationUserId}");
- sw.Close();
- }
- }
- }
- }
1.3, 修改操作过滤器类
修改后的操作过滤器类代码如下:
- using MVCCustomerActionFilterDemo.Models;
- using MVCCustomerActionFilterDemo.Util;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- namespace MVCCustomerActionFilterDemo.Extension
- {
- public class CustomerActionFilter : ActionFilterAttribute
- {
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法准备执行");
- string strControllerName = filterContext.RouteData.Values["controller"].ToString();
- string strActionName = filterContext.RouteData.Values["action"].ToString();
- LogEntity entity = new LogEntity()
- {
- OperationTime = DateTime.Now,
- ControllerName = strControllerName,
- ActionName = strActionName,
- // 为了方便测试写 admin, 真实案例需要获取当前登录的用户
- OperationUserId = "admin"
- };
- // 记录操作记录
- LogHelper.WriteOperRecore(entity);
- base.OnActionExecuting(filterContext);
- }
- public override void OnActionExecuted(ActionExecutedContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法执行结束");
- base.OnActionExecuted(filterContext);
- }
- public override void OnResultExecuting(ResultExecutingContext filterContext)
- {
- filterContext.HttpContext.Response.Write("Action 方法执行结束, 准备呈现视图");
- base.OnResultExecuting(filterContext);
- }
- public override void OnResultExecuted(ResultExecutedContext filterContext)
- {
- filterContext.HttpContext.Response.Write("视图呈现结束");
- base.OnResultExecuted(filterContext);
- }
- }
- }
运行程序, 查看生成的日志:
2, 实现权限控制功能
可以重写 OnActionExecuting 方法实现授权过滤器一样的功能, 因为 OnActionExecuting 方法是在 Action 方法执行前执行的, 自定义一个实现 ActionFilterAttribute 类的 CustomerActionPremisFilters 类, 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using MVCCustomerActionFilterDemo.DataBase;
- using MVCCustomerActionFilterDemo.Models;
- namespace MVCCustomerActionFilterDemo.Extension
- {
- public class CustomerActionPremisFilters :ActionFilterAttribute
- {
- public string ActionName { get; set; } // 用于保存 Action 配置的别名
- public string AreaName { get; set; }
- public string Roles { get; set; }
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
- // 如果未登录, 则跳转到登录界面
- if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
- {
- filterContext.HttpContext.Response.Redirect("/Account/LogOn");
- return;
- }
- // 当前登录用户的用户名
- string userName = filterContext.HttpContext.User.Identity.Name;
- // 当前登录用户对象
- User user = SampleData.users.Find(u => u.UserName == userName);
- if (user != null)
- {
- // 当前登录用户的角色
- Role role = SampleData.roles.Find(r => r.Id == user.RoleId);
- // 获得 controller:
- string controllerName = filterContext.RouteData.Values["controller"].ToString().ToLower();
- if (ActionName == null)
- {
- ActionName = filterContext.RouteData.Values["action"].ToString();
- }
- // 查询角色 id
- RoleWithControllerAction roleWithControllerAction = SampleData.roleWithControllerAndAction.Find(r => r.ControllerName.ToLower() == controllerName && ActionName.ToLower() == ActionName.ToLower());
- if (roleWithControllerAction != null)
- {
- // 有权限操作当前控制器和 Action 的角色 id
- this.Roles = roleWithControllerAction.RoleIds;
- }
- if (!string.IsNullOrEmpty(Roles))
- {
- foreach (string roleid in Roles.Split(','))
- {
- if (role.Id.ToString() == roleid)
- {
- //return 就说明有权限了, 后面的代码就不跑了, 直接返回视图给浏览器就好
- return;
- }
- }
- }
- filterContext.Result = new ViewResult { ViewName = "Error", };
- return;
- }
- else
- {
- filterContext.Result = new EmptyResult();
- filterContext.HttpContext.Response.Redirect("/Account/Logon", true);
- return;
- }
- }
- }
- }
新建 ActionPremisFilters 控制器, 代码如下:
- using MVCCustomerActionFilterDemo.Extension;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- namespace MVCCustomerActionFilterDemo.Controllers
- {
- public class ActionPremisFiltersController : Controller
- {
- // GET: ActionPremisFilters
- [CustomerActionPremisFilters]
- public ActionResult Index()
- {
- return View();
- }
- }
- }
修改 SampleData 数据, 使角色 id 为 2,3 的可以访问 ActionPremisFilters 的 Index 方法:
- using MVCCustomerActionFilterDemo.Models;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- namespace MVCCustomerActionFilterDemo.DataBase
- {
- /// <summary>
- /// 测试数据 (实际项目中, 这些数据应该从数据库拿)
- /// </summary>
- public class SampleData
- {
- public static List<User> users;
- public static List<Role> roles;
- public static List<RoleWithControllerAction> roleWithControllerAndAction;
- static SampleData()
- {
- // 初始化用户
- users = new List<User>()
- {
- new User(){ Id=1, UserName="jxl", RoleId=1},
- new User(){ Id=2, UserName ="senior1", RoleId=2},
- new User(){ Id=3, UserName ="senior2", RoleId=2},
- new User(){ Id=5, UserName="junior1", RoleId=3},
- new User(){ Id=6, UserName="junior2", RoleId=3},
- new User(){ Id=6, UserName="junior3", RoleId=3}
- };
- // 初始化角色
- roles = new List<Role>()
- {
- new Role() { Id=1, RoleName="管理员", Description="管理员角色"},
- new Role() { Id=2, RoleName="高级会员", Description="高级会员角色"},
- new Role() { Id=3, RoleName="初级会员", Description="初级会员角色"}
- };
- // 初始化角色控制器和 Action 对应类
- roleWithControllerAndAction = new List<RoleWithControllerAction>()
- {
- new RoleWithControllerAction(){ Id=1, ControllerName="AuthFilters", ActionName="AdminUser", RoleIds="1"},
- new RoleWithControllerAction(){ Id=2, ControllerName="AuthFilters", ActionName="SeniorUser",RoleIds="1,2"},
- new RoleWithControllerAction(){ Id=3, ControllerName="AuthFilters", ActionName="JuniorUser",RoleIds="1,2,3"},
- new RoleWithControllerAction(){ Id=3, ControllerName="AuthFilters", ActionName="Welcome",RoleIds="1,2"},
- new RoleWithControllerAction(){ Id=4, ControllerName="ActionFilters", ActionName="Index", RoleIds="2,3"},
- // 角色 2,3 可以访问 ActionPremisFilters 控制器的 Index 方法
- new RoleWithControllerAction(){ Id=4, ControllerName="ActionPremisFilters", ActionName="Index", RoleIds="2,3"}
- };
- }
- }
- }
修改配置文件
- <authentication mode="Forms">
- <forms loginUrl="~/Account/LogOn" timeout="2880" />
- </authentication>
测试, 访问 ActionPremisFilters 的 Index 方法, 由于系统还没有登录, 所以会跳转到登录页面, 这时候用 jxl 用户登录:
由于 jxl 用户没有访问 ActionPremisFilters 控制器中 Index 方法的权限, 所以会跳转到 Error 页面:
这时在用 senior1 用户登录, 由于 senior1 用户有权限访问, 所以会显示 Index 视图内容:
GitHub 代码地址:[email protected]:JiangXiaoLiang1988/MVCCustomerActionFilterDemo.Git
来源: http://www.bubuko.com/infodetail-3059416.html