ASP.NET MVC中Filter过滤器的使用

MVC Filter是典型的AOP(面向切面编程)应用,在ASP.NET MVC中的4个过滤器类型,如下:

但是默认实现它们的过滤器只有三种,分别是ActionFilter(方法),Authorize(授权),HandleError(错误处理)。

  • 特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
  • 特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性
    Filter的默认实现方式就是带有Attribute后缀的,有了Attribute,我们就可以将我们的Filter像标签一样的贴在方法或者控制器的上方

Web Api: System.Web.Http.Filters.ActionFilterAttribute 继承该类

  • OnActionExecuting 执行前
  • OnActionExecuted 执行后
  • OnResultExecuting 返回前
  • OnResultExecuted 返回后

Mvc: System.Web.Mvc.ActionFilterAttribute 继承该类

  • OnActionExecuting 执行前
  • OnActionExecutingAsync 执行前(异步)
  • OnActionExecuted 执行后
  • OnActionExecutedAsync 执行后(异步)

1、Action过滤器在ASP.NET MVC中创建MvcApp项目,创建文件夹Filter,然后新建类MyActionFilterAttribute(为了遵循默认的约定,名称以Attribute结尾),继承自ActionFilterAttribute类。ActionFilterAttribute类有如下4个方法。

using System;
 
namespace System.Web.Mvc
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
    {
 
        protected ActionFilterAttribute();
 
        public virtual void OnActionExecuted(ActionExecutedContext filterContext);
 
        public virtual void OnActionExecuting(ActionExecutingContext filterContext);
 
        public virtual void OnResultExecuted(ResultExecutedContext filterContext);
 
        public virtual void OnResultExecuting(ResultExecutingContext filterContext);
    }
}

实现MyActionFilterAttribute类:

using System;
using System.Web.Mvc;
 
namespace MvcApp.Filter
{
    public class MyActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //1、获取请求的类名和方法名
            string strController = filterContext.RouteData.Values["controller"].ToString();
            string strAction = filterContext.RouteData.Values["action"].ToString();
 
            //2、用另一种方式获取请求的类名和方法名
            string strController2 = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
            string strAction2 = filterContext.ActionDescriptor.ActionName;
 
            filterContext.HttpContext.Response.Write("控制器:" + strController + "<br/>");
            filterContext.HttpContext.Response.Write("控制器:" + strController2 + "<br/>");
            filterContext.HttpContext.Response.Write("Action:" + strAction + "<br/>");
            filterContext.HttpContext.Response.Write("Action:" + strAction2 + "<br/>");
 
            filterContext.HttpContext.Response.Write("Action执行前:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff") + "<br/>");
            base.OnActionExecuting(filterContext);
        }
 
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.HttpContext.Response.Write("Action执行后:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff") + "<br/>");
            base.OnActionExecuted(filterContext);
        }
    }
}

对于过滤器,我们可以把它们加在3个地方,一个是控制器上面(控制器下面的所有Action),一个是Action上面(指定标识的Action),另一个就是全局位置(所有控制器中的Action)。这里只演示在Action上面和Home控制器中:

[MyActionFilter]
public ActionResult Index()
{
    return View();
}

2、Result过滤器

新建MyResultFilterAttribute类,继承ActionFilterAttribute:

using System;
using System.Web.Mvc;
 
namespace MvcApp.Filter
{
    public class MyResultFilterAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 加载"视图"前执行
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            filterContext.HttpContext.Response.Write("加载视图前执行 OnResultExecuting" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff") + "<br/>");
            base.OnResultExecuting(filterContext);
        }
 
        /// <summary>
        /// 加载"视图"后执行
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            filterContext.HttpContext.Response.Write("加载视图后执行 OnResultExecuted" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff") + "<br/>");
            base.OnResultExecuted(filterContext);
        }
    }
}

这里把MyResultFilter过滤器加在控制器上面,相当于给Home控制器中的所有的Action方法添加了MyResultFilter过滤器。

using MvcApp.Filter;
using System.Web.Mvc;
 
namespace MvcApp.Controllers
{
    [MyResultFilter]
    public class HomeController : Controller
    {
        [MyActionFilter]
        public ActionResult Index()
        {
            return View();
        }
 
    }
}

3、AuthorizeAttribute过滤器

创建MyAuthorizeAttribute类,继承AuthorizeAttribute类:

using System.Web.Mvc;
 
namespace MvcApp.Filter
{
    /// <summary>
    /// 授权过滤器
    /// </summary>
    public class MyAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            filterContext.HttpContext.Response.Write("OnAuthorization<br/>");
 
            //注释掉父类方法,因为父类里的OnAuthorization方法会调用ASP.NET的授权验证机制
            //base.OnAuthorization(filterContext);
        }
 
    }
}

在控制器Home中的Index上添加MyAuthorize过滤器:

[MyAuthorize]
[MyActionFilter]
public ActionResult Index()
{
    return View();
}

通常Authorize过滤器也是在全局过滤器上面的,主要用来做登录验证或者权限验证,在App_Start目录下的FilterConfig类的RegisterGlobalFilters方法中添加:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //filters.Add(new HandleErrorAttribute());
 
        //添加全局授权过滤器
        filters.Add(new MyAuthorizeAttribute());
    }
}

在全局中注册过滤器,则所有控制器的所有行为(Action)都会执行这个过滤器。

运行结果,如下图:

4、Exception过滤器

创建MyHandleErrorAttribute类,继承HandleErrorAttribute类:

using System;
using System.Web.Mvc;
 
namespace MvcApp.Filter
{
    /// <summary>
    /// 异常处理过滤器
    /// </summary>
    public class MyHandleErrorAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext filterContext)
        { 
            //1、获取异常对象
            Exception ex = filterContext.Exception;
            
            //2、记录异常日志
 
            //3、重定向友好页面
            filterContext.Result = new RedirectResult("~/error.html");
 
            //4、标记异常已经处理完毕
            filterContext.ExceptionHandled = true;
 
            base.OnException(filterContext);
        }
    }
}

在Action上面添加MyHandleError过滤器:

[MyHandleError]
public ActionResult GetErr()
{
    int a = 0;
    int b = 1 / a;
    return View();
}

创建异常错误友好提示页面error.html。

<body>
    自定义错误页面
</body>

运行会自动跳转到error.html页面。

如果页面没有跳转,就需要去Web.config配置文件中的<system.web>节点下面添加如下配置节点,开启自定义错误:

<customErrors mode="On"></customErrors>

通常这样的异常处理是放在全局过滤器上面的,只要任意Action方法报错就会执行MyHandleError过滤器中的代码。

修改App_Start目录下面的FilterConfig类:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //filters.Add(new HandleErrorAttribute());
 
        //添加全局授权过滤器
        filters.Add(new MyAuthorizeAttribute());
 
        //添加全局异常处理过滤器
        filters.Add(new MyHandleErrorAttribute());
    }
}

Global.asax下的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
 
namespace MvcApp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}
相关推荐
初晴~43 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱581361 小时前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳1 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾1 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
小林coding3 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者3 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu
V+zmm101343 小时前
基于小程序宿舍报修系统的设计与实现ssm+论文源码调试讲解
java·小程序·毕业设计·mvc·ssm
从善若水3 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust
机器之心4 小时前
终于等来能塞进手机的文生图模型!十分之一体量,SnapGen实现百分百的效果
人工智能·后端