ASP.NET Core MVC — MVC执行概貌(1) IActionInvoker的构建

IActionInvoker是通过IActionInvokerFactory工厂构建的。

当构建IActionInvoker时,IActionInvokerFactory会遍历自身的IActionInvokerProvider列表,依次调用provider.OnProvidersExecuting(context);然后再按逆序遍历该列表,依次调用provider.OnProvidersExecuted(context);,从而形成如下的层级式调用:

1
2
3
4
5
6
7
8
9
10
provider0.OnProvidersExecuting(context);
provider1.OnProvidersExecuting(context);
provider2.OnProvidersExecuting(context);
...
...
...
...
provider2.OnProvidersExecuted(context);
provider1.OnProvidersExecuted(context);
provider0.OnProvidersExecuted(context);

最后,当所有这些都处理完成之后,返回ActionInvokerProviderContextResult属性。

这里的ActionInvokerProviderContext是一个非常简单的上下文变量,该类只包含了ActionContext(输入)和IActionInvoker(输出)两个属性:

1
2
3
4
5
6
7
8
9
10
11
public class ActionInvokerProviderContext
{
public ActionInvokerProviderContext(ActionContext actionContext)
{
if (actionContext == null) { /* throw */ }
ActionContext = actionContext;
}

public ActionContext ActionContext { get; }
public IActionInvoker Result { get; set; }
}

上文说到ActionInvokerFactory会先以正序遍历自身的IActionInvokerProvider,然后再反序遍历,其源码实现非常简单:

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
internal class ActionInvokerFactory : IActionInvokerFactory
{
private readonly IActionInvokerProvider[] _actionInvokerProviders;

public ActionInvokerFactory(IEnumerable<IActionInvokerProvider> actionInvokerProviders)
{
_actionInvokerProviders = actionInvokerProviders.OrderBy(item => item.Order).ToArray();
}

public IActionInvoker CreateInvoker(ActionContext actionContext)
{
var context = new ActionInvokerProviderContext(actionContext);

foreach (var provider in _actionInvokerProviders)
{
provider.OnProvidersExecuting(context);
}

for (var i = _actionInvokerProviders.Length - 1; i >= 0; i--)
{
_actionInvokerProviders[i].OnProvidersExecuted(context);
}

return context.Result;
}
}

IActionInvoker可以有多种子类实现。比如,MVC中的ControllerActionInvokerRazorPage中的PageActionInvoker是最常见的实现。
由于MVC相交于RazorPage有更广为人知,这个笔记系列以ControllerActionInvoker为例,分析IActionInvoker的构建与执行。

为了获取ControllerActionInvoker实例,我们还需要ControllerActionInvokerProvider,其核心代码是:

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
internal class ControllerActionInvokerProvider : IActionInvokerProvider
{
private readonly ControllerActionInvokerCache _controllerActionInvokerCache; // 注入
private readonly ILogger _logger; // 注入
private readonly DiagnosticListener _diagnosticListener; // 注入
private readonly IActionResultTypeMapper _mapper; // 注入
private readonly IActionContextAccessor _actionContextAccessor; // 注入
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories; // 注入MvcOptions 获取
private readonly int _maxModelValidationErrors; // 注入MvcOptions 获取

...

public void OnProvidersExecuting(ActionInvokerProviderContext context)
{
if (context == null) { /*throw*/ }
if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor)
{
var controllerContext = new ControllerContext(context.ActionContext) {
ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories)
};
controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;
var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext);
var invoker = new ControllerActionInvoker( _logger, _diagnosticListener, _actionContextAccessor, _mapper, controllerContext, cacheEntry, filters);

context.Result = invoker;
}
}

public void OnProvidersExecuted(ActionInvokerProviderContext context) { }
}

可以看到,ControllerActionInvoker的过滤器都是优先从缓存中加载,然后简单地new一个ControllerActinInvoker实例。