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

  • 相关阅读:
    mojoportal学习——文章翻译之超过3个内容面板
    mojoportal学习——文章翻译之mojoportal的皮肤模板
    mojoportal中替换第一层菜单中的连接为空连接
    mojoportal学习——文章翻译之不同的模块使用不同的风格
    mojoportal学习——文章翻译之layout.master文件
    mojoportal学习——文章翻译之mojoportal高级功能之菜单子站点化
    GridView选中行变色(无刷新)
    页面之间传递参数的几种方法荟萃
    ASP.NET程序中常用的三十三种代码(一)
    ASP.NET设计应用程序的七大绝招
  • 原文地址:https://www.cnblogs.com/dotnet261010/p/10859908.html
Copyright © 2011-2022 走看看