asp.net core mvc剖析:动作执行
紧跟上一篇文章。通过路由和动作匹配后,最终会得到跟当前请求最匹配的一个ActionDescriptor,然后通过IActionInvoker执行动作。
我们先来看一下IActionInvoker如何得到,代码如下:
context.Handler = (c) =>
{
var routeData = c.GetRouteData();
//根据actiondescriptor实例化ActionContext对象
var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);
if (_actionContextAccessor != null)
{
_actionContextAccessor.ActionContext = actionContext;
}
//创建IActionInvoker
var invoker = _actionInvokerFactory.CreateInvoker(actionContext);
if (invoker == null)
{
throw new InvalidOperationException(
Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
actionDescriptor.DisplayName));
}
//执行invoker处理请求
return invoker.InvokeAsync();
};
从上面的代码可以看到,一个IActionInvoker是通过IActionInvokerFactory创建,框架里该接口实现类是ActionInvokerFactory,该类CreateInvoker方法实现代码如下:
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;
}
在方法中,首先实例化一个ActionInvokerProviderContext,然后调用IActionInvokerProvider来设置context.Result,context.Result就是一个IActionInvoker,所以我们跟踪下框架中IActionInvokerProvider实现类,来看看它里面是如何工作的。框架中的提供了一个实现类ControllerActionInvokerProvider,在OnProvidersExecuting方法中创建了IActionInvoker对象,代码如下:
public void OnProvidersExecuting(ActionInvokerProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
} var actionDescriptor = context.ActionContext.ActionDescriptor as ControllerActionDescriptor; if (actionDescriptor != null)
{
var controllerContext = new ControllerContext(context.ActionContext);
// PERF: These are rarely going to be changed, so let's go copy-on-write.
controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories);
controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors; var cacheState = _controllerActionInvokerCache.GetState(controllerContext); context.Result = new ControllerActionInvoker(
_controllerFactory,
_argumentBinder,
_logger,
_diagnosticSource,
controllerContext,
cacheState.Filters,
cacheState.ActionMethodExecutor);
}
}
从上面的代码我们可以看到,最终就是实例化了一个ControllerActionInvoker对象,该类构造方法前几个参数不再介绍了,重点是最后两个参数,一个是IFilterMetadata[],表示跟当前动作相关联的过滤器信息集合,一个是ObjectMethodExecutor,从名字上可以看出,这个就是控制器方法的执行器。而这两个参数来自cacheState,这个对象是通过调用_controllerActionInvokerCache.GetState(controllerContext)得到的,它是一个ControllerActionInvokerState结构体类型,定义如下:
public struct ControllerActionInvokerState
{
public ControllerActionInvokerState(
IFilterMetadata[] filters,
ObjectMethodExecutor actionMethodExecutor)
{
Filters = filters;
ActionMethodExecutor = actionMethodExecutor;
}
//动作过滤器集合
public IFilterMetadata[] Filters { get; }
//方法执行器
public ObjectMethodExecutor ActionMethodExecutor { get; set; }
}
cacheState创建的过程如下:
public ControllerActionInvokerState GetState(ControllerContext controllerContext)
{
var cache = CurrentCache;
var actionDescriptor = controllerContext.ActionDescriptor; IFilterMetadata[] filters;
Entry cacheEntry;
if (!cache.Entries.TryGetValue(actionDescriptor, out cacheEntry))
{
var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext);
filters = filterFactoryResult.Filters; var executor = ObjectMethodExecutor.Create(
actionDescriptor.MethodInfo,
actionDescriptor.ControllerTypeInfo); cacheEntry = new Entry(filterFactoryResult.CacheableFilters, executor);
cacheEntry = cache.Entries.GetOrAdd(actionDescriptor, cacheEntry);
}
else
{
// Filter instances from statically defined filter descriptors + from filter providers
filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.FilterItems);
} return new ControllerActionInvokerState(filters, cacheEntry.ActionMethodExecutor);
}
主要看下第10行到第15行代码。首先通过FilterFactory获取到跟当前动作相关联的过滤器信息集合,然后通过ObjectMethodExecutor.Create创建一个ObjectMethodExecutor对象,创建好后进行缓存。
其实到这里我们已经知道IActionInvokder就是一个ControllerActionInvoker,然后调用该对象的InvokeAsync方法开始动作执行。最新版本实现采用了状态机(如果概念上错误,欢迎大家拍砖指正)的特点,执行时在不同状态之间进行切换,最终完成处理。而这部分工作就是在Next方法里实现的。方法真正执行实在State.ActionInside状态时执行,在此状态下通过调用InvokeActionMethodAsync完成控制器方法的执行,主要代码如下:
//下面的代码就是根据方法返回值类型不同,实现不同的逻辑
var returnType = executor.MethodReturnType;
if (returnType == typeof(void))
{
executor.Execute(controller, orderedArguments);
result = new EmptyResult();
}
else if (returnType == typeof(Task))
{
await (Task)executor.Execute(controller, orderedArguments);
result = new EmptyResult();
}
else if (executor.TaskGenericType == typeof(IActionResult))
{
result = await (Task<IActionResult>)executor.Execute(controller, orderedArguments);
if (result == null)
{
throw new InvalidOperationException(
Resources.FormatActionResult_ActionReturnValueCannotBeNull(typeof(IActionResult)));
}
}
else if (executor.IsTypeAssignableFromIActionResult)
{
if (_executor.IsMethodAsync)
{
result = (IActionResult)await _executor.ExecuteAsync(controller, orderedArguments);
}
else
{
result = (IActionResult)_executor.Execute(controller, orderedArguments);
} if (result == null)
{
throw new InvalidOperationException(
Resources.FormatActionResult_ActionReturnValueCannotBeNull(_executor.TaskGenericType ?? returnType));
}
}
else if (!executor.IsMethodAsync)
{
var resultAsObject = executor.Execute(controller, orderedArguments);
result = resultAsObject as IActionResult ?? new ObjectResult(resultAsObject)
{
DeclaredType = returnType,
};
}
else if (executor.TaskGenericType != null)
{
var resultAsObject = await executor.ExecuteAsync(controller, orderedArguments);
result = resultAsObject as IActionResult ?? new ObjectResult(resultAsObject)
{
DeclaredType = executor.TaskGenericType,
};
}
else
{
// This will be the case for types which have derived from Task and Task<T> or non Task types.
throw new InvalidOperationException(Resources.FormatActionExecutor_UnexpectedTaskInstance(
executor.MethodInfo.Name,
executor.MethodInfo.DeclaringType));
} _result = result; }
无论是哪种返回值类型,都是通过调用executor.Execute完成控制器方法调用,并获取结果,executor就是上面提到的ObjectMethodExecutor,我们只看下Execute方法实现:
public object Execute(object target, object[] parameters)
{
return _executor(target, parameters);
}
这里面又是一个_executor,它是一个delegate object ActionExecutor(object target, object[] parameters)委托类型,它是在实例化ObjectMethodInvoker时通过GetExecutor方法创建的,采用的是动态lambda表达式,有了这个委托对象,就可以执行控制器方法了。
方法执行后,会得到方法返回结果,然后进入Result相关状态流程,直到State.ResultInside状态时,调用InvokeResultAsync方法执行动作结果,具体代码如下:
protected async Task InvokeResultAsync(IActionResult result)
{
var actionContext = _actionContext; _diagnosticSource.BeforeActionResult(actionContext, result); try
{
await result.ExecuteResultAsync(actionContext);
}
finally
{
_diagnosticSource.AfterActionResult(actionContext, result);
}
}
到这里一个控制器动作就算执行完了,需要指出的是文章只介绍了主体流程,还有很多其他执行状态文章中没提到。
asp.net core mvc剖析:动作执行的更多相关文章
- asp.net core mvc剖析:启动流程
asp.net core mvc是微软开源的跨平台的mvc框架,首先它跟原有的MVC相比,最大的不同就是跨平台,然后又增加了一些非常实用的新功能,比如taghelper,viewcomponent,D ...
- 解说asp.net core MVC 过滤器的执行顺序
asp.net core MVC 过滤器会在请求管道的各个阶段触发.同一阶段又可以注册多个范围的过滤器,例如Global范围,controller范围等.以ActionFilter为例,我们来看看过滤 ...
- asp.net core mvc剖析:路由
在mvc框架中,任何一个动作请求都会被映射到具体控制器中的方法上,那框架是如何完成这样一个过程的,现在我们就来简单分析下流程. 我们紧跟上面的主题,任何一个请求都会交给处理管道进行处理,那mvc处理的 ...
- asp.net core mvc剖析:mvc执行过程(一)
前面介绍了路由的过程,我们再来看下MvcRouteHandler的代码: public Task RouteAsync(RouteContext context) { ...... //根据路由信息查 ...
- asp.net core mvc剖析:mvc动作选择
一个http请求过来后,首先经过路由规则的匹配,找到最符合条件的的IRouter,然后调用IRouter.RouteAsync来设置RouteContext.Handler,最后把请求交给RouteC ...
- asp.net core mvc剖析:KestrelServer
KestrelServer是基于Libuv开发的高性能web服务器,那我们现在就来看一下它是如何工作的.在上一篇文章中提到了Program的Main方法,在这个方法里Build了一个WebHost,我 ...
- asp.net core mvc剖析:处理管道构建
在启动流程文章中提到,在WebHost类中,通过BuildApplication完成http请求处理管道的构建.在来看一下代码: ...... //这个调用的就是Startup.cs类中的Config ...
- net core mvc剖析:启动流程
net core mvc剖析:启动流程 asp.net core mvc是微软开源的跨平台的mvc框架,首先它跟原有的MVC相比,最大的不同就是跨平台,然后又增加了一些非常实用的新功能,比如taghe ...
- 创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图
创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图 创建CRUD动作方法及视图 参照VS自带的基架(Scaffold)系统-MVC Controller with view ...
随机推荐
- java_XML_JAXB
JAXB 可以实现Java对象与XML的相互转换,在JAXB中,将一个Java对象转换为XML的过程称之为Marshal,将XML转换为Java对象的过程称之为UnMarshal. 下面使用的是JDK ...
- matlab 向量法建数组(推荐)
一.用赋值的方法可以扩展一个已知的数组: arr= 1:1:4; arr(8)=6; 此时,arr = 1 2 3 4 0 0 0 6 arrNew=arr; 此时arrNew = 1 2 3 4 0 ...
- 提升iOS审核通过率之“IPv6兼容测试”
作者:jingle 腾讯系统测试工程师 商业转载请联系腾讯WeTest授权,非商业转载请注明出处. 原文链接:http://wetest.qq.com/lab/view/285.html 一.背景 在 ...
- api接口json串换行
1.问题描述:在后台输入框中明明回车换行了,但是返回到 app客户端显示出来的 确实带有 \n 这个时候无论怎么调试都不行: 2.铺垫:大家都知道 php输出字符串的时候 使用 单引号 比使用 双 ...
- python2.6.6安装MySQL-python模块正确方法
Centos6.5 x64 系统python2.6.6安装MySQL-python模块 一.安装前准备 操作系统:centos6.5 x64 ·安装python和python开发工具 python p ...
- IOS高级开发~开机启动&无限后台运行&监听进程
一般来说, IOS很少给App后台运行的权限. 仅有的方式就是 VoIP. IOS少有的为VoIP应用提供了后台socket连接,定期唤醒并且随开机启动的权限.而这些就是IOS上实现VoIP App的 ...
- Pro Git CN Plus
Git — The stupid content tracker, 傻瓜内容跟踪器.Linus 是这样给我们介绍 Git 的. Git 是用于 Linux 内核开发的版本控制工具.与常用的版本控制工具 ...
- [翻译]现代java开发指南 第二部分
现代java开发指南 第二部分 第二部分:部署.监控 & 管理,性能分析和基准测试 第一部分,第二部分 =================== 欢迎来到现代 Java 开发指南第二部分.在第一 ...
- 每日java基础知识(01)
1.java语言的主要特点. 跨平台性:一个应用可以不经过修改,就直接在不同的平台上运行. 面向对象:java是面向对象的语言,可以使用对象封装事物的属性和行为,可以使用面向对象的思想进行分析设计,并 ...
- 第23篇 js快速学习知识
前面说了js的一些高级方面的基础知识,这些都是比较容易出错的和比较难理解的东西,除了这些之外其它的知识都比较简单了,基础学好了,扩展起来就是小意思.今天说说js方面可以快速学习和入门的知识. 1.闭包 ...