第四节:MVC中AOP思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法
一. 简介
MVC中的过滤器可以说是MVC框架中的一种灵魂所在,它是MVC框架中AOP思想的具体体现,所以它以面向切面的形式无侵入式的作用于代码的业务逻辑,与业务逻辑代码分离,一经推出,广受开发者的喜爱。
那么过滤器到底是什么呢?它又有什么作用呢?
用户通过URL访问Web系统不一定都能得到相应的内容,一方面不同的用户权限不同,另一方面是为了保护系统,防止被攻击,这就是过滤器的核心所在,我们总计一下过滤器都有哪些作用:
①:判断用户是否登录以及不同用户对应不同的权限问题。
②:防盗链、防爬虫。
③:系统中语言版本的切换(本地化和国际化)。
④:权限管理系统中动态Action。
⑤:决策输出缓存。
知道到了过滤器的作用,那么过滤器分哪几类呢?如下图1:
二. 执行顺序
从上图①可知,过滤器分四类,总共重写了六个方法,在这六个方法里可以处理相应的业务逻辑,那么如果四种过滤器的六个重写方法同时存在,它们的执行顺序是什么呢?
首先要将OnException方法除外,该方法不和其余五个方法参与排序问题,该方法独立存在,什么时间报错,什么时候调用。
其余三种过滤器中的五个重写方法的执行顺序:
三. 自定义实现形式
1. 直接在控制器中重写方法或者利用控制器间的继承
新建任何一个控制器,它均继承Controller类,F12进入Controller类中,发现Controller类中已经实现了过滤器需要实现的接口,并且提供虚方法供我们重写,代码如下:
基于以上原理,这样在控制器级别上我们就有两种思路来实现过滤器。
方案一:直接在当前控制器重写相应的过滤器方法,则该过滤器的方法作用于当前控制器的所有Action。
方案二:新建一个父类控制器,在父类控制器中重写过滤器的方法,然后子类控制器继承该父类控制器,则该该过滤器作用于子类控制器中的所有Action。
【该方法和接下来以特性的形式作用于控制器的效果是一致的】
/// <summary>
/// 控制器继承该控制器,和特性作用在控制器上效果一致
/// </summary>
public class MyBaseFilterController : Controller
{
//需要用protected类型,不能用public类型
protected override void OnAuthorization(AuthorizationContext filterContext)
{
//1.如果保留如下代码,则会运行.net framework定义好的身份验证,如果希望自定义身份验证,则删除如下代码
// base.OnAuthorization(filterContext); //2.获取区域名字
// string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); //3.获取控制器作用的Controller和action的名字
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower();
string actionName = filterContext.ActionDescriptor.ActionName.ToLower();
filterContext.HttpContext.Response.Write("身份验证过滤器作用于" + controllerName + "控制器下的" + actionName + "方法</br>");
}
}
2. 自定义类继承MVC中过滤器实现类或过滤器接口,特性的形式作用于控制器或Action
特别补充:MVC框架中的AuthorizeAttirbute、ActionFilterAttribute、HandleErrorAttribute类已经实现了过滤器对应的接口,所以我们在自定义过滤器的时候,可以直接继承以上三个类;或者实现相应的接口:IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilter。(该方案在实现相应接口的同时,需要继承FilterAttribute,使自定义的类成为一个特性)。
下面以继承MVC中实现类的形式来自定义四种过滤器:
A:身份验证过滤器
/// <summary>
/// 身份验证过滤器
/// 1. 在非MVC框架项目中使用MVC过滤器,需要通过nuget把MVC的程序集添加进去
/// 2. 继承AuthorizeAttribute类,然后对OnAuthorization方法进行 override 覆写
/// 3. 在Action运行之前首先运行该过滤器
/// </summary>
public class MyAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
//1.如果保留如下代码,则会运行.net framework定义好的身份验证,如果希望自定义身份验证,则删除如下代码
// base.OnAuthorization(filterContext); //2.获取区域名字
// string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); //3.获取控制器作用的Controller和action的名字
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower();
string actionName = filterContext.ActionDescriptor.ActionName.ToLower();
filterContext.HttpContext.Response.Write("身份验证过滤器作用于" + controllerName + "控制器下的" + actionName + "方法</br>");
}
}
B: 行为过滤器
/// <summary>
/// 行为过滤器
/// 1. 在非MVC框架项目中使用MVC过滤器,需要通过nuget把MVC的程序集添加进去
/// 2. 继承ActionFilterAttribute类,然后对OnActionExecuting方法和OnActionExecuted方法进行 override 覆写
/// 3. OnActionExecuting方法:在action方法运行之前,且OnAuthorization过滤器运行之后调用
/// OnActionExecuted方法:在action方法运行之后调用
/// </summary>
public class MyAction: ActionFilterAttribute
{ /// <summary>
/// 在action方法运行之前调用
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//1.如果保留如下代码,则会运行.net framework定义好的行为验证,如果希望自定义行为验证,则删除如下代码
// base.OnActionExecuting(filterContext); //2.获取区域名字
// string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); //3.获取控制器作用的Controller和action的名字
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower();
string actionName = filterContext.ActionDescriptor.ActionName.ToLower();
filterContext.HttpContext.Response.Write("行为过滤器OnActionExecuting作用于" + controllerName + "控制器下的" + actionName + "方法运行之前</br>");
}
/// <summary>
/// 在action方法运行之后调用
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//1.如果保留如下代码,则会运行.net framework定义好的行为验证,如果希望自定义行为验证,则删除如下代码
// base.OnActionExecuted(filterContext); //2.获取区域名字
// string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); //3.获取控制器作用的Controller和action的名字
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower();
string actionName = filterContext.ActionDescriptor.ActionName.ToLower();
filterContext.HttpContext.Response.Write("行为过滤器OnActionExecuted作用于" + controllerName + "控制器下的" + actionName + "方法运行之后</br>");
}
}
C:结果过滤器
/// <summary>
/// 结果过滤器
/// 1. 在非MVC框架项目中使用MVC过滤器,需要通过nuget把MVC的程序集添加进去
/// 2. 继承ActionFilterAttribute类,然后对OnResultExecuting方法和OnResultExecuted方法进行 override 覆写
/// 3. OnResultExecuting方法:在执行结果之后(action之后),页面渲染之前调用
/// OnResultExecuted方法:在页面渲染之后调用
/// </summary>
public class MyResult : ActionFilterAttribute
{ /// <summary>
/// action执行之后(OnActionExecuting之后),页面渲染之前调用
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
//1.如果保留如下代码,则会运行.net framework定义好的结果验证,如果希望自定义结果验证,则删除如下代码
// base.OnResultExecuting(filterContext); //该方法中无法获取是哪个控制器后
filterContext.HttpContext.Response.Write("结果过滤器OnResultExecuting作用于action运行之后,页面加载之前");
}
/// <summary>
/// 页面渲染之后调用
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
//1.如果保留如下代码,则会运行.net framework定义好的结果验证,如果希望自定义结果验证,则删除如下代码
// base.OnResultExecuted(filterContext); //该方法中无法获取是哪个控制器后
filterContext.HttpContext.Response.Write("结果过滤器OnResultExecuted作用于页面渲染之后");
}
}
D:异常过滤器
使用自定义异常处理,需要在web.config中为system.web添加<customErrors mode="On" />节点
/// <summary>
/// 异常过滤器
/// 需要注意的点:
/// ①:如果自定义异常过滤器且需要有作用于全局,需要把FilterConfig中的 filters.Add(new HandleErrorAttribute());注释掉,
/// 然后把自定义的异常过滤器添加到FilterConfig中。
/// ②:使用自定义异常处理,需要在web.config中为system.web添加<customErrors mode="On" />节点
/// </summary>
public class MyException: HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//调用框架本身异常处理器的方法
base.OnException(filterContext); //获取异常信息(可以根据实际需要写到本地或数据库中)
var errorMsg = filterContext.Exception; //跳转指定的错误页面
filterContext.Result = new RedirectResult("/error.html");
}
}
下面展示以特性的形式作用于控制器或控制器中的Action:
3. 自定义类继承MVC中实现类或接口,全局注册,作用于全部控制器
如果以上两种方式均不能满足你的过滤器的使用范围,你可以在App_Start文件夹下的FilterConfig类中进行全局注册,使该过滤器作用于所有控制器中所有Action方法。
特别注意的一点是:自定义异常过滤器,需要把系统默认的filters.Add(new HandleErrorAttribute());注释掉。
全局注册的代码如下:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//如果自定义异常过滤器,需要把默认的异常过滤器给注释掉
//filters.Add(new HandleErrorAttribute()); //自定义异常过滤器
filters.Add(new MyException()); //全局注册身份验证、行为、结果过滤器
//filters.Add(new MyAuthorize());
//filters.Add(new MyAction());
//filters.Add(new MyResult()); //全局注册登录验证(暂时注释,使用的时候要打开)
//filters.Add(new CheckLogin());
}
}
四. 结合实际案例进行代码测试
1. 测试过滤器的执行顺序
将上面的身份验证过滤器、行为过滤器、结果过滤器以特性的形式作用于Action上,通过断点监控或者查看最后的输出结果:
结果:
符合:OnAuthorization→OnActionExecuting-> Action方法执行 ->OnActionExecuted->OnResultExecuting/ -> Render View() (页面渲染加载)->OnResultExecuted() 这一顺序。
2. 全局捕获异常,记录错误日志案例
步骤1:编写异常过滤器,通过 var errorMsg = filterContext.Exception; 获取异常信息,可以写入文本、存入数据库、或者是Log4Net错误日志框架进行处理。代码在上面。
步骤2:在web.config中为system.web添加<customErrors mode="On" />节点。
步骤3:添加到全局注册文件中进行捕获。
步骤4:在自定义的异常过滤器中添加断点,并且自己制造一个错误。
捕获到错误,进行页面跳转。
3. 登录验证案例
业务背景:在90%以上的Web系统中,很多页面都是登录成功以后才能看到的,当然也有很多页面不需要登录,对于需要登录才能看到的页面,即使你知道了访问地址,也是不能访问的,会退出到登录页面提示让你登录,对于不需要登录的页面通过URL地址可以直接访问。
分析:针对以上背景,过滤器对于大部分Action是需要过滤的,需要做登录验证,对于一小部分是不需要过滤的。
解决思路:
①:自定义一个身份验证过滤器,进行全局注册。
②:自定义一个Skip特性,将该特性加到不需要身份验证的Action上。
③:重点说一下身份证过滤器中的逻辑:
a. 先判断该Action上是否又Skip特性,如果有,停止不继续执行;如果没有,继续下面的验证逻辑。
b. 从Session中或Redis中读取当前用户,查看是否为空,如果为空,表明没有登录,返回到登录页面;如果不为空,验证通过,进行后面的业务逻辑。
代码段如下:
/// <summary>
/// 校验系统是否登录的过滤器
/// 使用身份验证过滤器进行编写
/// </summary>
public class CheckLogin: AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
//1. 校验是否标记跨过登录验证
if (filterContext.ActionDescriptor.IsDefined(typeof(skipAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(skipAttribute), true))
{
//表示该方法或控制器跨过登录验证
return;
}
//2. 校验是否登录
//可以使Session或数据库或nosql
//这里只是测试,所有统统当做没有登录来处理
var sessionUser = HttpContext.Current.Session["CurrentUser"];//使用session
if (sessionUser == null)
{
HttpContext.Current.Session["CurrentUrl"] = filterContext.RequestContext.HttpContext.Request.RawUrl;
//如果没有登录,则跳转到错误页面
filterContext.Result = new RedirectResult("/error.html");
}
}
}
第四节:MVC中AOP思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法的更多相关文章
- ASP.Net Core 5.0 MVC中AOP思想的体现(五种过滤器)并结合项目案例说明过滤器的用法
执行顺序 使用方法,首先实现各自的接口,override里面的方法, 然后在startup 类的 ConfigureServices 方法,注册它们. 下面我将代码贴出来,照着模仿就可以了 IActi ...
- 第一节: Timer的定时任务的复习、Quartz.Net的入门使用、Aop思想的体现
一. 前奏-Timer类实现定时任务 在没有引入第三方开源的定时调度框架之前,我们处理一些简单的定时任务同时都是使用Timer类, DotNet中的Timer类有三个,分别位于不同的命名空间下,分别是 ...
- MVC中控制器向视图传值的四种方式
MVC中的控制器向视图传值有四种方式分别是 1 ViewDate 2.ViewBag 3.TempDate 4.Model 下面分别介绍四种传值方式 首先先显示出控制器中的代码 using S ...
- spring框架中AOP思想与各种配置详解
Spring中提供两种AOP支持: 1.基于代理的经典AOP 2.Aspectj注解配置AOP 首先我们先了解什么是AOP,AOP(Aspect Oriented Programming ...
- MVC中提交表单的4种方式
一,MVC HtmlHelper方法 Html.BeginForm(actionName,controllerName,method,htmlAttributes){} BeginRouteForm ...
- [ExtJS5学习笔记]第二十四节 Extjs5中表格gridpanel或者表单数据后台传输remoteFilter设置
本文地址:http://blog.csdn.net/sushengmiyan/article/details/39667533 官方文档:http://docs.sencha.com/extjs/5. ...
- [ExtJS5学习笔记]第十四节 Extjs5中data数据源store和datapanel学习
本文地址:http://blog.csdn.net/sushengmiyan/article/details/39031383 sencha官方API:http://docs.sencha.com/e ...
- 《linux就该这么学》第四节课笔记,三章和四章开始!
第三章 (根据课本和在线培训视频排版总结,借鉴请改动) 右键可打开终端练习 3.1:输入输出重定向 输入重定向:符号 "<" ,是一种 ...
- Spring MVC中返回JSON数据的几种方式
我们都知道Spring MVC 的Controller方法中默认可以返回ModeAndView 和String 类型,返回的这两种类型数据是被DispatcherServlet拿来给到视图解析器进行继 ...
随机推荐
- GitHub的初级使用
最近准备学习一个GitHub的使用 一.账号创建 1.百度找到GitHub官方网站(https://github.com/ ) 2.点击Sign up注册GitHub账号 下图为注册页面 第一步:填写 ...
- LeetCode算法题-Relative Ranks(Java实现)
这是悦乐书的第248次更新,第261篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第115题(顺位题号是506).根据N名运动员的得分,找到他们的相对等级和得分最高的三个 ...
- DISK 100% BUSY,谁造成的?(ok)
iostat等命令看到的是系统级的统计,比如下例中我们看到/dev/sdb很忙,如果要追查是哪个进程导致的I/O繁忙,应该怎么办? # iostat -xd ... Device: rrqm/s wr ...
- webpack开发环境和生产环境切换原理
在package.json中有如下设置: "scripts": { "dev": "node build/dev-server.js" ...
- error C2381: “exit”: 重定义;__declspec(noreturn) 不同
问题: error C2381: “exit” : 重定义:__declspec(noreturn) 不同 解决办法: 调换一下头文件的包含次序: #include <GL/glut.h> ...
- Linux内核入门到放弃-虚拟文件系统-《深入Linux内核架构》笔记
VFS的任务并不简单.一方面,它用来提供了一种操作文件.目录及其他对象的统一方法.另一方面,它必须能够与各种方法给出的具体文件系统的实现达成妥协,这些实现在具体细节.总体设计方面都有一些不同之处. 文 ...
- python小白——进阶之路——day3天-———运算符
(1)算数运算符: + - * / // % ** (2)比较运算符: > < >= <= == != (3)赋值运算符: = += -= *= /= //= %= ** ...
- CentOS7.5修改字符集
乱码产生的原因: 计算机中储存的信息都是用二进制数表示的:而我们在屏幕上看到的英文.汉字等字符是二进制数转换之后的结果.通俗的说,按照何种规则将字符存储在计算机中,如'a'用什么表示,称为" ...
- asp.net core 2.1认证
asp.net core 2.1认证 这篇文章基于asp.net core的CookieAuthenticationHandler来讲述. 认证和授权很相似,他们的英文也很相似,一个是Authenti ...
- [转帖]如何重置CentOS/RHEL 7中遗忘的根用户帐户密码
如何重置CentOS/RHEL 7中遗忘的根用户帐户密码 https://www.cnblogs.com/swordxia/p/4389466.html 作者的blog质量很高呢 没看完 但是感觉 很 ...