zoukankan      html  css  js  c++  java
  • MVC过滤器:自定义操作过滤器

    一、操作过滤器

    1、定义

    操作过滤器用于实现IActionFilter接口以及包装操作方法执行。IActionFilter接口声明两个方法:OnActionExecuting和OnActionExecuted。OnActionExecuting在操作方法之前运行。OnActionExecuted在操作方法之后运行,可以执行其他处理,如向操作方法提供额外数据、检查返回值或取消执行操作方法。

    查看ActionFilterAttribute类的定义:

    #region 程序集 System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
    // D:PracticeMVC自定义操作过滤器MVCCustomerActionFilterDemopackagesMicrosoft.AspNet.Mvc.5.2.7lib
    et45System.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代码地址:git@github.com:JiangXiaoLiang1988/MVCCustomerActionFilterDemo.git

  • 相关阅读:
    关于JSON可能出现的错误,待更/todo
    mongoose的安装与使用(书签记录) 2017
    HTTP的学习记录3--HTTPS和HTTP
    HTTP的学习记录(二)头部
    HTTP(一)概述
    LeetCode 455. Assign Cookies
    LeetCode 453. Minimum Moves to Equal Array Elements
    LeetCode 448. Find All Numbers Disappeared in an Array
    LeetCode 447. Number of Boomerangs
    LeetCode 416. Partition Equal Subset Sum
  • 原文地址:https://www.cnblogs.com/dotnet261010/p/10859908.html
Copyright © 2011-2022 走看看