博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通过源码了解ASP.NET MVC 几种Filter的执行过程
阅读量:6150 次
发布时间:2019-06-21

本文共 7228 字,大约阅读时间需要 24 分钟。

一、前言

  之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了。其实阅读源码是个很好的习惯,它不只停留在知道怎么用的阶段,而是让我们知道一系列的为什么,为什么这样设计,为什么这样使用...。很多朋友应该看过《asp.net x 框架揭秘》这本书,确实不错,特别是边看源码边看书,可以有不小的收获。Ok,我不是大神,我只是心血来潮想看一下源码!

二、几种常见的Filter

  说到mvc里的Filter,自然会想到IAuthorizationFilter,IActionFilter,IResultFilter,IExceptionFilter,搜索一下也都知道怎么用了。其实说白了,这些接口定义了一系列方法,这些方法在请求的不同时机被执行,所谓Filter,就是让我们可以在不同时机进行拦截处理。

     这里还涉及到一个特性:FilterAttribute,例如常用的AuthorizeAttribute就继承了FilterAttribute和实现了IAuthorizationFilter接口。说到Attribute,马上会关联到:运行时、反射、性能。框架会在运行过程中,通过反射获取标记属性,并执行特定的操作;至于性能问题,通常可以通过缓存来优化。

  所以,我们可以做出猜测,以AuthorizeAttribute为例,msdn说它可以进行权限验证,也就是在Action执行前,框架会通过反射获取标记在Action(或Controller)上的FilterAttribute,并执行IAuthorizationFilter定义的OnAuthorization方法,在该方法内部进行权限验证。所以如果我们要在Action执行前做某些判断或处理,可以 1.定义一个Attribute继承FilterAttribute,并实现IActionFilter接口(与IAuthorizationFilter不同的是,这个时候ModelBinding已经完成);2.实现IActionFilter中的方法;3.标记在Action(或Controller上)。ok,下面就通过源码来验证这个过程。

三、源码分析

  Action的执行是由ActionInvoker负责的,我们直接从这里出发。IActionInvoker定义了ActionInvoker要实现的方法,该接口定义如下:  

1
2
3
4
public 
interface 
IActionInvoker
{
    
bool 
InvokeAction(ControllerContext controllerContext, 
string 
actionName);
}

  ControllerActionInvoker  实现了该接口,顾名思义,它用于执行Controller 的 Action方法。它的 InvokeAction如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public 
virtual 
bool 
InvokeAction(ControllerContext controllerContext, 
string 
actionName)
{
    
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
    
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
    
if 
(actionDescriptor != 
null
)
    
{
        
//标记1
        
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
 
        
try
        
{
            
//标记2
            
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);
 
            
if 
(authenticationContext.Result != 
null
)
            
{
                
InvokeActionResult(controllerContext, authenticationContext.Result);
            
}
            
else
            
{
                
IPrincipal principal = authenticationContext.Principal;
 
                
if 
(principal != 
null
)
                
{
                    
Thread.CurrentPrincipal = principal;
                    
HttpContext.Current.User = principal;
                
}
 
                
//标记3
                
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
                
if 
(authorizationContext.Result != 
null
)
                
{
                    
AuthenticationChallengeContext challengeContext =
                        
InvokeAuthenticationFiltersChallenge(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor, authorizationContext.Result);                          
                    
InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
                
}
                
else
                
{
                    
if 
(controllerContext.Controller.ValidateRequest)
                    
{
                        
ValidateRequest(controllerContext);
                    
}
 
                    
//标记4
                    
IDictionary<
string
object
> parameters = GetParameterValues(controllerContext, actionDescriptor);
                    
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
                    
//标记5
                    
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
                
}
            
}
        
}
        
catch 
(Exception ex)
        
{
            
//标记6
            
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
            
if 
(!exceptionContext.ExceptionHandled)
            
{
                
throw
;
            
}
            
InvokeActionResult(controllerContext, exceptionContext.Result);
        
}
 
        
return 
true
;
    
}
    
return 
false
;
}

  其实这里的6个标记已经印证了我们的猜测,先获取各种Filter,然后在各个时机执行它们。上面标记2-6都是InvokeXXXFilters就是具体的执行方法。

  但是,到这里上面我们说到的FilterAttribute还没有出现。我们先把焦点放到标记1,GetFilters 上,它获取一个FilterInfo。GetFilters的定义如下:

1
2
3
4
protected 
virtual 
FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
    
return 
new 
FilterInfo(_getFiltersThunk(controllerContext, actionDescriptor));
}

  _getFiltersThunk 是一个私有变量:

1
private 
Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = FilterProviders.Providers.GetFilters;

  通过定义可以看出,_getFiltersThunk 会返回一个Filter 集合(这里的Filter是一个实际的类,而上面提到的是概念性的东西,或者叫过滤器更合适),Filter 对象包装了IXXXFilter接口对象,具体是在其Instance 属性中。这里有点绕,但不影响,简单的说就是 GetFilters 方法会根据 FilterProviders.Providers.GetFilters 返回的一个IEnumerable<Filter>包装一个 FilterInfo对象。

  我们先看 IEnumerable<Filter> 是如何获取的,它通过 FilterProviders.Providers.GetFilters 获得,FilterProviders 定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
public 
static 
class 
FilterProviders
{
    
static 
FilterProviders()
    
{
        
Providers = 
new 
FilterProviderCollection();
        
Providers.Add(GlobalFilters.Filters);
        
Providers.Add(
new 
FilterAttributeFilterProvider());
        
Providers.Add(
new 
ControllerInstanceFilterProvider());
    
}
 
    
public 
static 
FilterProviderCollection Providers { 
get
private 
set
; }
}

  这里可以注册自定义的FilterProvider,FilterProvider实际是实现了IFilterProvider(定义了GetFilters方法)的类。可以看到,mvc 默认已经准备两个FilterProvider。调用GetFilters实际会遍历每一个FilterProvider的GetFilters方法,以内置的FilterAttributeFilterProvider 为例,它的 GetFilters方法如下:

1
2
3
4
5
6
7
8
9
10
public 
virtual 
IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
    
ControllerBase controller = controllerContext.Controller;
    
var 
typeFilters = GetControllerAttributes(controllerContext, actionDescriptor)
        
.Select(attr => 
new 
Filter(attr, FilterScope.Controller, 
null
));
    
var 
methodFilters = GetActionAttributes(controllerContext, actionDescriptor)
        
.Select(attr => 
new 
Filter(attr, FilterScope.Action, 
null
));
 
    
return 
typeFilters.Concat(methodFilters).ToList();
}

  这里也可以看到,Filter对象包装了具体的过滤器。其中GetControllerAttributes,实际它会调用ControllerDescriptor的 GetFilterAttribute,该方法定义如下:

1
2
3
4
public 
virtual 
IEnumerable<FilterAttribute> GetFilterAttributes(
bool 
useCache)
{
    
return 
GetCustomAttributes(
typeof
(FilterAttribute), inherit: 
true
).Cast<FilterAttribute>();
}

  ok,FilterAttribute 终于出现了!GetActionAttributes 也是类似的过程。

     获取到Controller和Action的FilterAttribute,并包装成Filter集合后,就会构建一个FilterInfo对象,该对象的作用可以从其构造函数看出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public 
FilterInfo(IEnumerable<Filter> filters)
{
    
// evaluate the 'filters' enumerable only once since the operation can be quite expensive
    
var 
cache = filters.ToList();
 
    
var 
overrides = cache.Where(f => f.Instance 
is 
IOverrideFilter);
 
    
FilterScope actionOverride = SelectLastScope<IActionFilter>(overrides);
    
FilterScope authenticationOverride = SelectLastScope<IAuthenticationFilter>(overrides);
    
FilterScope authorizationOverride = SelectLastScope<IAuthorizationFilter>(overrides);
    
FilterScope exceptionOverride = SelectLastScope<IExceptionFilter>(overrides);
    
FilterScope resultOverride = SelectLastScope<IResultFilter>(overrides);
 
    
_actionFilters.AddRange(SelectAvailable<IActionFilter>(cache, actionOverride));
    
_authenticationFilters.AddRange(SelectAvailable<IAuthenticationFilter>(cache, authenticationOverride));
    
_authorizationFilters.AddRange(SelectAvailable<IAuthorizationFilter>(cache, authorizationOverride));
    
_exceptionFilters.AddRange(SelectAvailable<IExceptionFilter>(cache, exceptionOverride));
    
_resultFilters.AddRange(SelectAvailable<IResultFilter>(cache, resultOverride));
}

  很明显,它将Filter 按照各自IXXXFilter接口进行分类。在InvokeAction方法内,就是根据这个分类,在各个时机进行相应的调用的。

四、总结

  通过一张图来简单总结一下:

转载于:https://www.cnblogs.com/webenh/p/7692799.html

你可能感兴趣的文章
HTML 邮件链接,超链接发邮件
查看>>
HDU 5524:Subtrees
查看>>
手机端userAgent
查看>>
pip安装Mysql-python报错EnvironmentError: mysql_config not found
查看>>
http协议组成(请求状态码)
查看>>
怎样成为一个高手观后感
查看>>
[转]VC预处理指令与宏定义的妙用
查看>>
JQuery radio单选框应用
查看>>
MySql操作
查看>>
python 解析 XML文件
查看>>
MySQL 文件导入出错
查看>>
java相关
查看>>
由一个异常开始思考springmvc参数解析
查看>>
用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识
查看>>
Pandas时间序列
查看>>
开发者论坛一周精粹(第四十八期) ICP经营许可证办理流程
查看>>
基于Go的websocket消息服务
查看>>
流计算独享模式正式邀测
查看>>
hibernate笔记--缓存机制之 二级缓存(sessionFactory)和查询缓存
查看>>
Ceph,TFS,FastDFS,MogileFS,MooseFS,GlusterFS 对比
查看>>