楠槡

ASP.NET MVC 过滤器

2018-04-05

过滤器是 ASP.NET MVC 中很重要的一个功能,过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响应内容,只响应特定内容给那些有特定权限的用户。

简述过滤器

过滤器理论上有以下功能:

  • 判断登录与否或用户权限
  • 决策输出缓存
  • 防盗链
  • 防蜘蛛
  • 本地化与国际化设置
  • 实现动态Action(做权限管理系统的好东西)

实现过滤器的两种方法:

  • 自定义类继承自相应的类或借口,重写方法,作为特性使用
  • 在控制器类中重写方法

    特性方法的使用:

    • 作为Controller或Action的特性
    • 在Global中注册为全局过滤器,应用于所有的Controller的Action

    注意:如果继承自接口,则需要让类实现FilterAttribute,才能作为特性使用。参数类均继承自ControllerContext,主要包含属性请求上下文,路由数据,结果。

过滤器的四种实现

四种常用的过滤器:
过滤器类型 | 接口 | 默认实现 | 描述
:–:|:–:|:–:|:–:
Authorization | IAuthorization | AuthorizeAttribute | 首先运行,在其他过滤器的操作方法
Action | IActionFilter | ActionFilterAttribute | 运行之前和之后的动作方法
Result | IResultFilter | ActionFilterAttribute | 运行前后执行的操作结果
Exception | IExceptionFilter | HandleErrorAttribute | 如果只运行一个过滤器,操作方法或行动结果抛出一个异常

MVC5 中新增了 Authentication 过滤器,用于验证。在 MVC5 中,将原本的 Authorization 拆分为 Authentication(身份验证)和 Authorization(授权)两个,MVC 接受到 Web 页面请求时,首先将执行所有的 Authentication 过滤器,如果请求需要认证且已经被验证过了,就会继续下一步处理,若没有通过身份验证,则会停止处理。

下面先说下身份过滤验证器,然后以身份过滤验证器为例介绍几种实现过滤器的方法,再介绍后续的其他的过滤器!

身份验证过滤器

定义身份验证的过滤器,需要在约束的Action执行前来执行,重写OnAuthorization方法。

通过一个简单的实例,来展示Filter的模式:

创建过滤器

现在项目中添加一个 Filter 文件夹来存储过滤器,然后来写第一个 Filter 示例 - MyAuthorization.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System.Web.Mvc;
namespace STU_mvc.Areas.UserManage.Filters
{
public class MyAuthorization: AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
//.net framework 默认身份验证
//base.OnAuthorization(filterContext);
//跳转页面,使用Result,而不是使用Response.Redirect() 因为它会继续执行Action
//filterContext.Result = new RedirectResult(UrlHelper.GenerateUrl("","","","","","",true));
//获取路由数据,当前上下文匹配到路由规则后,得到的一个对象
//filterContext.RouteData
//获取上下文
filterContext.HttpContext.Response.Write("Test");
}
}
}

注意: 这是一个身份验证的 Filter ,因此这个类必须继承自System.Web.Mvc 中的 AuthorizeAttribute ,并对OnAuthorization()方法进行重写。

特性模式调用控制器

写好了 Filter 还得去调用他,调用 Filter 的方法就是,在 Action 前为其申明一个特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using STU_mvc.Areas.UserManage.Filters;
using System.Web.Mvc;
namespace STU_mvc.Areas.UserManage.Controllers
{
public class HomeController : Controller
{
// GET: UserManage/Home
[MyAuthorization]
public ActionResult Index()
{
return View();
}
}
}

如上代码中的控制器直接返回了一个 Test ,编译-运行-访问当前路由,会发现浏览器在 html 代码前直接附加了一个 Test ,由此可知,过滤器发生在 Action 之前。

直接返回上下文

注意:Filter 是作为特性而附加给 Action 的,因此可以被不同的 Action 重复调用,且只在调用后才会执行。同样,如果一个 Controller 中的所有 Action 都需要使用这个 Filter ,则可以直接给当前 Controller 类附加上这个 Filter 特性即可。

其他的过滤器调用方法

全局过滤器

Global.asxs 中 FilterConfig 调用了 App_Start 中的 FilterConfig.cs 文件,这个文件在全局中申明了过滤器,及所有在此处注册的过滤器,会被所有 Controller 的 所有 Action 执行。

全局 Fliter

1
2
3
4
5
6
7
8
9
10
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
//在全局中注册过滤器,则所有控制器的所有行为都会执行
filters.Add(new MyAuthorization());
}
}

RegisterGlobalFilters() 方法的形参表面,filters 是一个集合,因此只需要在其中使用 filters 的 Add 方法申明过滤器即可。

重写方法实现过滤

针对某一控制器进行重写,应用范围为当前控制器下的所有的行为。

在控制器中 输入 override OnAuthentication 自动补全,为当前控制器重写该方法:

1
2
3
4
protected override void OnAuthentication(AuthenticationContext filterContext)
{
filterContext.HttpContext.Response.Write("test for controller");
}

重写完成后,调试可以发现, 被重写OnAuthenticationhome 控制器执行了 filter 的内容,而未被重写的test 控制器则不受影响。

coverride

同样的,也可以添加一个继承自Controller 的基类,并重写基类的OnAuthentication ,之后让控制器不再继承Controller,而是间接继承基类。 即: HomeController -> BaseController -> Controller ,通过为 BaseController 重写 filter 来实现过滤器。

异常处理过滤器

异常处理过滤器,用于当发生异常时,进行自定义异步处理,记录日志,跳转页面等。

创建过滤器

首先创建过滤器 MyException.cs,同样的,在 Filter 文件夹下新建一个类,继承自 HandleErrorAttribute

注意:OnException 的 base 特性不能删除,否则捕获不到异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace STU_mvc.Areas.UserManage.Filters
{
public class MyExcepition: HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
//OnException 的 base 特性不能删除,否则捕获不到异常
base.OnException(filterContext);
//记录日志
//直接跳转到异常处理页面
filterContext.Result= new RedirectResult("/Error/400.html");
}
}
}

全局注册异常过滤器

然后使用全局注册,通常来说,异常处理都是全局有效的:

注意:如果要实现自定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//系统默认的异常处理过滤器
//filters.Add(new HandleErrorAttribute());
//在全局中注册过滤器,则所有控制器的所有行为都会执行
//filters.Add(new MyAuthorization());
//添加自己的异常处理过滤器
filters.Add(new MyExcepition());
}
}

配置启动自定义异常处理

注意: 要使用自定义的异常处理,需要在 web.config中为 system.web添加<customErrors mode ="on" /> 节点,开启自定义异常。

1
2
3
<system.web>
<customErrors mode ="On" defaultRedirect="Error/400.html"/>
……

exception

配置完成后,我们随便输入一个不匹配的路由,就会返回到我们提前编写好的400页面,并通过 url 暴露出异常的路由信息,而正常的路由请求则不受此影响。

##行为过滤器

Action 过滤器继承ActionFilterAttribute实现,并提供了在两个不同时间点执行代码的能力,分别在 行为执行前(OnActionExecuting)/行为执行后(OnActionExecuted) 执行相关操作。

创建过滤器

Filter 目录下新建一个类MyAction,并继承ActionFilterAttribute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyAction: ActionFilterAttribute
{
//行为执行前
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write("This line is Before Action execute!");
//base.OnActionExecuting(filterContext);
}
//行为执行后,结果输出前
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write("This line is After Action execute!");
//base.OnActionExecuted(filterContext);
}
}

添加特性

同样的,添加特性使其生效

1
2
3
4
5
6
7
8
9
10
public class HomeController : Controller
{
// GET: UserManage/Home
[MyAction]
public ActionResult Index()
{
Response.Write("Action Execut");
return View();
}
}

效果如下:

action

return 的 view() 是在 Action 完成后执行的,所以会先显示两个 filter 的结果,再返回 view(),而 Action 中的内容则在二者之间显示。

结果过滤器

同样继承自ActionFilterAttribute实现,并提供了在两个不同时间点执行代码的能力,分别在 结果执行前(On ResultExecuting)/结果执行后(OnResultExecuted) 执行相关操作。

创建结果过滤器

首先创建过滤器 MyResult.cs,同样的,在 Filter 文件夹下新建一个类,继承自 ActionFilterAttribute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyResult: ActionFilterAttribute
{
//结果执行前执行
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write("Before Result<bt />");
}
//结果执行后执行
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write("After Result<bt />");
}
}

添加特性

同样的,添加特性使其生效

1
2
3
4
5
6
7
8
9
10
public class HomeController : Controller
{
// GET: UserManage/Home
[MyResult]
public ActionResult Index()
{
Response.Write("Action Execut");
return View();
}
}

效果如下:

result

Tags: dotNET
使用支付宝打赏
使用微信打赏

若你觉得我的文章对您有帮助,点击上方按钮请我喝杯咖啡☕

若文章中存在问题,或您有任何意见和疑问,均可与我联系

扫描二维码,分享此文章 📱