ASP.Net MVC默认的过滤器

来源:互联网 发布:苹果提示无法加入网络 编辑:程序博客网 时间:2024/05/17 08:53


3.1 过滤器初步


  大一点的项目总会有相关的AOP面向切面的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中Action在执行前或者执行后我们想做一些特殊的操作(比如身份验证,日志,异常,行为截取等),而不想让MVC开发人员去关心和写这部分重复的代码。那么,我们可以通过AOP截取实现,而在MVC项目中我们就可以直接使用它提供的Filter的特性帮我们解决,不用自己实现复杂的AOP了。
AOP:Aspect Oriented Programming(AOP)是较为热门的一个话题。AOP,国内大致译作“面向切面编程”。针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
  利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

3.2 微软提供的几种默认过滤器


微软默认为我们提供了四种类型的过滤器(Filter),如下图所示:


这里,我们主要来看看ActionFilter(Action过滤器)和ExceptionFilter(异常过滤器)的使用:

(1)Action Filter

ActionFilterAttribute默认实现了IActionFilter和IResultFilter。而ActionFilterAttribute是一个Abstract的类型,所以不能直接使用,因为它不能实例化,所以我们想使用它必须继承一下它然后才能使用。


①因此,我们首先在Models中新建一个类,取名为:MyActionFilterAttribute(以Attribute结尾比较符合编码规范),并使其继承自ActionFilterAttribute,然后重写基类所提供的虚方法:

public class MyActionFilterAttribute : ActionFilterAttribute{    public string Name { get; set; }     /// <summary>    /// Action 执行之前先执行此方法    /// </summary>    /// <param name="filterContext">过滤器上下文</param>    public override void OnActionExecuting(ActionExecutingContext filterContext)    {        base.OnActionExecuting(filterContext);        HttpContext.Current.Response.Write("OnActionExecuting :" + Name);    }     /// <summary>    /// Action执行之后    /// </summary>    /// <param name="filterContext">过滤器上下文</param>    public override void OnActionExecuted(ActionExecutedContext filterContext)    {        base.OnActionExecuted(filterContext);        HttpContext.Current.Response.Write("OnActionExecuted :" + Name);    }     /// <summary>    /// ActionResult执行之前先执行此方法    /// </summary>    /// <param name="filterContext">过滤器上下文</param>    public override void OnResultExecuting(ResultExecutingContext filterContext)    {        base.OnResultExecuting(filterContext);        HttpContext.Current.Response.Write("OnResultExecuting :" + Name);     }     /// <summary>    /// ActionResult执行之后先执行此方法    /// </summary>    /// <param name="filterContext">过滤器上下文</param>    public override void OnResultExecuted(ResultExecutedContext filterContext)    {        base.OnResultExecuted(filterContext);        HttpContext.Current.Response.Write("OnResultExecuted :" + Name);    }}


这里我们重写了四个虚方法,他们各自代表了在Action执行之前和之后需要执行的业务逻辑,以及在Result执行之前和之后需要执行的业务逻辑。这里的Result主要是指我们在Action中进行return 语句返回结果时(例如:return Content(“Hello Filter!”);),之前和之后要执行的逻辑处理。

比如:我们想要在每个Action执行之前进行用户是否登录的校验,可以在OnActionExecuting中判断用户Session是否存在,如果存在则继续执行Action的具体业务代码,如果不存在则重定向页面到登陆页,后边的Action业务代码不再执行。

②现在有了自定义的过滤器,我们怎么将其应用到Action中呢?这里有三种方式:

一是给某个控制器的某个Action指定此Filter:

[MyActionFilter(Name = "Filter Action")]public ActionResult Filter(){    Response.Write("<p>Action正在努力执行中...</p>");    return Content("<p>OK:视图成功被渲染</p>");}


二是给某个控制器的所有Action指定此Filter:

[MyActionFilter(Name="Home Filter")]public class HomeController : Controller{}


但是,要注意的是:如果既给Controller指定了Filter,又给该Controller中的某个Action指定了Filter,那么具体的这个Action以离其定义最近的Filter为准,也就是一个优先级的顺序问题:Action的Filter优先级高于Controller的Filter。

三是给此项目中的所有控制器即全局指定此Filter:在App_Start中更改FilterConfig类,此种方式优先级最低

public static void RegisterGlobalFilters(GlobalFilterCollection filters){    filters.Add(new HandleErrorAttribute());    // 注册自定义Action过滤器:优先级最低,但是可以作用到所有的控制器和Action    filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });}


③现在我们来看看具体的效果:

可以看到,我们的/Home/Filter这个Action中只有两句代码,一句Response.Write,另一句是return Content();在Response.Write之前执行了OnActionExecuting的过滤器方法,之后则执行了OnActionExecuted的过滤器方法;我们刚刚说了,在Action中的return语句代表了Result,那么在Result之前执行了OnResultExecuting过滤器方法,之后则执行了OnResultExecuted过滤器方法。这里仅仅是为了展示,在实际开发中是需要写一些具体的业务逻辑处理的,例如:判断用户的登录状态,记录用户的操作日志等等。

(2)Exception Filter

①同样,在Models中新建一个类,取名为:MyExceptionFilterAttribute,并使其继承自HandleErrorAttribute。


public class MyExceptionFilterAttribute : HandleErrorAttribute{    public override void OnException(ExceptionContext filterContext)    {        base.OnException(filterContext);         HttpContext.Current.Response.Redirect("/Home/Index");    }}


这里,重写基类的OnException方法,这里仅仅为了演示效果,没有对异常进行处理。在实际开发中,需要获取异常对象,并将其记录至日志中。例如,下面一段代码:


public override void OnException(ExceptionContext filterContext)   {       base.OnException(filterContext);       //获取系统异常消息记录       string strException = filterContext.Exception.Message;       if (!string.IsNullOrEmpty(strException))       {           //使用Log4Net记录异常信息           Exception exception = filterContext.Exception;           if (exception != null)           {               LogHelper.WriteErrorLog(strException, exception);           }           else           {               LogHelper.WriteErrorLog(strException);           }       }   filterContext.HttpContext.Response.Redirect("~/GlobalErrorPage.html");   }


②有了异常过滤器,我们怎么来应用到项目中呢?答案也在App_Start中,还是在FilterConfig类中,新添一句代码进行注册:


public class FilterConfig{    public static void RegisterGlobalFilters(GlobalFilterCollection filters)    {        filters.Add(new HandleErrorAttribute());        // 注册自定义Action过滤器:优先级最低,但是可以作用到所有的控制器和Action        filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });        // 注册自定义Exception过滤器        filters.Add(new MyExceptionFilterAttribute());    }}


③为了测试,我们新增一个Action,使其能够出现一个异常:DividedByZero


public ActionResult Exception(){    int a = 10;    int b = 0;    int c = a / b;    return Content("Exception is happened.");}

④当我们测试这个Action时,会发现系统执行了自定义的异常过滤器,将我们的这个请求改为重定向到Index这个Action了。


如何一次性给所有action做登录验证过滤,如何排除不需要做登录验证的action?


1. 如何让某个Action未登录就不能执行?
先封装一个判断是否登录的函数。

目标Action中调用这个函数,如果未登录,跳转登录Action

2. 如果Controller中有很多Action都要处理怎么办?
可以自定义一个过滤器

给每个需要做登录检查的action打上这个标签

3. 这样还是麻烦,每个Action都要打标签吗?
也可以给Controller这个层级打标签,对内部所有Action都有效

4. 如果项目中有很多Controller,每个都要打一遍吗?
有两个方法:
4.1.声明一个BaseController:Controller,在这个基类上打标签

让需要做登录验证的Controller都继承自BaseController

4.2使用全局过滤器

应用启动的时候注册全局过滤器,会对所有Controller的Action起作用

5. 可是我的首页和登录Action是不需要做过滤的,怎么排除?
修改过滤器,增加一个是否需要检查的属性

只给不需要做检查的Action或者Controller打上不需要做检查的标签即可

全局过滤器注册的时候,要注册为需要检查

6. 回头审视一下我们的基类BaseController
继承自Controller类,它本身已经继承了IActionFilter接口,我们只要override这个接口的方法,就可以了。所有继承自这个基类的Controller就都可以做登录验证了,基类也不需要打标签了,相当于把过滤器的业务逻辑,转移到这个方法中:

在子类中控制是否需要做登录检查

这个方法的问题是:没有办法对子类的Action单独控制是否做检查。
非要做也可以,那就要再定义一个标签Atrribute,来标识不需要检查,在BaseController父类中通过反射检查action是否打了这个不需要检查的标签。
但是这样有点绕了,不如用全局过滤器方便!

1 0
原创粉丝点击