MVC过滤器:自定义操做过滤器

1、操做过滤器

一、定义

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

查看ActionFilterAttribute类的定义:git

#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个方法执行的顺序了:github

OnActionExecuting是Action执行前的操做、OnActionExecuted则是Action执行后的操做、OnResultExecuting是解析ActionResult前执行、OnResultExecuted是解析ActionResult后执行。
即:Action执行前:OnActionExecuting方法先执行→Action执行 →OnActionExecuted方法执行→OnResultExecuting方法执行→返回的ActionRsult中的 executeResult方法执行→OnResultExecuted执行。数据库

二、案例

2.一、建立自定义操做过滤器

新建一个自定义过滤器,而后从新里面的方法,代码以下:浏览器

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.二、新建控制器

建立一个控制器,用来测试自定义操做过滤器,代码以下:框架

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方法对应的视图代码以下:ide

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div> 
        <h1>操做过滤器测试页面</h1>
    </div>
</body>
</html>

 运行结果;测试

2、结果过滤器

一、定义

结果筛选器用于实现IResultFilter接口以及包装ActionResult对象的执行。IResultFilter接口声明两个方法OnResultExecuting和OnResultExecuted。OnResultExecuting在执行ActionResult对象以前运行。OnResultExecuted在结果以后运行,能够对结果执行其余处理,如修改 HTTP 响应。this

结果过滤器也是实现了ActionFilterAttribute类。spa

二、案例

修改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);
        }
    }
}

 运行结果:

3、案例

一、记录操做

在真实项目中,能够利用操做过滤器记录哪一个用户登陆系统之后进行了哪些操做。

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.二、建立日志类

建立日志帮助类,代码以下:

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.三、修改操做过滤器类

修改后的操做过滤器类代码以下:

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);
        }
    }
}

运行程序,查看生成的日志:

 

二、实现权限控制功能

能够重写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为二、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"},
                // 角色二、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

相关文章
相关标签/搜索