一. 简介

  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思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法的更多相关文章

  1. ASP.Net Core 5.0 MVC中AOP思想的体现(五种过滤器)并结合项目案例说明过滤器的用法

    执行顺序 使用方法,首先实现各自的接口,override里面的方法, 然后在startup 类的 ConfigureServices 方法,注册它们. 下面我将代码贴出来,照着模仿就可以了 IActi ...

  2. 第一节: Timer的定时任务的复习、Quartz.Net的入门使用、Aop思想的体现

    一. 前奏-Timer类实现定时任务 在没有引入第三方开源的定时调度框架之前,我们处理一些简单的定时任务同时都是使用Timer类, DotNet中的Timer类有三个,分别位于不同的命名空间下,分别是 ...

  3. MVC中控制器向视图传值的四种方式

    MVC中的控制器向视图传值有四种方式分别是 1 ViewDate  2.ViewBag   3.TempDate  4.Model 下面分别介绍四种传值方式 首先先显示出控制器中的代码 using S ...

  4. spring框架中AOP思想与各种配置详解

    Spring中提供两种AOP支持:   1.基于代理的经典AOP   2.Aspectj注解配置AOP    首先我们先了解什么是AOP,AOP(Aspect Oriented Programming ...

  5. MVC中提交表单的4种方式

    一,MVC  HtmlHelper方法 Html.BeginForm(actionName,controllerName,method,htmlAttributes){} BeginRouteForm ...

  6. [ExtJS5学习笔记]第二十四节 Extjs5中表格gridpanel或者表单数据后台传输remoteFilter设置

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39667533 官方文档:http://docs.sencha.com/extjs/5. ...

  7. [ExtJS5学习笔记]第十四节 Extjs5中data数据源store和datapanel学习

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39031383 sencha官方API:http://docs.sencha.com/e ...

  8. 《linux就该这么学》第四节课笔记,三章和四章开始!

    第三章 (根据课本和在线培训视频排版总结,借鉴请改动)         右键可打开终端练习             3.1:输入输出重定向 输入重定向:符号 "<" ,是一种 ...

  9. Spring MVC中返回JSON数据的几种方式

    我们都知道Spring MVC 的Controller方法中默认可以返回ModeAndView 和String 类型,返回的这两种类型数据是被DispatcherServlet拿来给到视图解析器进行继 ...

随机推荐

  1. SAP Change Request Management (ChaRM)基础教程

    本文介绍了SAP Solution Manager中的变更请求管理工具(Change Request Management,以下简称ChaRM ). 最近打算写个上线前请求号检查工具,为此需要了解相关 ...

  2. syso快捷键设置

    syso快捷键

  3. 总结JAVA----IO流中的File类

    对于IO流中File类的总结 File类的基本概念 File类只能用于完成对于文件属性(是否存在.可读性.长度)的一些操作,不能用于文件的访问. File类的对象 File类的对象存储的是文件的绝对路 ...

  4. 01背包问题(动态规划)python实现

    01背包问题(动态规划)python实现 在01背包问题中,在选择是否要把一个物品加到背包中.必须把该物品加进去的子问题的解与不取该物品的子问题的解进行比較,这样的方式形成的问题导致了很多重叠子问题, ...

  5. c# 7.0 6.0 新语法

    1.参考地址:https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/exploration/csharp-7?tutorial-step=5 ...

  6. printf 函数原型

    typedef char *va_list; #define _AUPBND (sizeof (acpi_native_int) - 1) #define _ADNBND (sizeof (acpi_ ...

  7. Facebook第三方网页登录(JavaScript SDK)

    文档网址:https://developers.facebook.com/docs/facebook-login/web#logindialog 一.应用配置  https://www.faceboo ...

  8. 用python实现的一个自动聊天的机器人

    因为之前想过 如果每天早上微信能够发送天气预报给我,给我老婆多好,然后就动手看网上的教程做了一个可以定时发送天气预报的程序, 最近又想到折腾,做了一个更加详细的版本.但是需要主动操作 具体操作看图. ...

  9. HTML多图无缝循环翻页效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. Jquery2--属性相关的操作

    知识点总结 1.属性 属性(如果你的选择器选出了多个对象,那么默认只会返回出第一个属性). attr(属性名|属性值) - 一个参数是获取属性的值,两个参数是设置属性值 - 点击加载图片示例 remo ...