ASP.NET Core MVC 源码学习:MVC 启动流程详解
前言
在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习。
ASP.NET Core 是新一代的 ASP.NET 应用程序,它是跨平台的,并且不依赖于 IIS,新的 MVC Core 设计加入了依赖注入和模块化的 Http 处理管道,这篇文章我们一起通过源码看一下它的启动过程,每一步都很重要。
我们知道 MVC Core 是作为一个中间件程序,注册到 ASP.NET Core 管道流程中的,我先来回顾一下在以前基于 IIS 的传统 ASP.NET 程序。 传统的 ASP.NET 应用程序由可执行文件 InetMgr.exe (IIS 宿主进程)创建,然后调用受托管的应用程序入口,接着调用 HttpApplication.Application_Start() 进一步初始化,通常情况下,我们的初始化代码都写在 Application_StartGlobal.asax 中。
我们今天的主题是MVC 框架,所以针对 ASP.NET Core Host 和 Server 的初始化流程就不详细讲解了,由兴趣的同学可以翻看一下我的这篇文章。
ASP.NET Core 源码地址:https://github.com/aspnet/mvc
Getting StartedASP.NET Core MVC 源码程序主要包含几部分组成:
从 Startup 说起ASP.NET Core MVC 程序在启动之后,会经过一系列流程,然后到达 Microsoft.AspNetCore.Mvc包里的扩展程序 IMvcBuilder AddMvc(this IServiceCollection services) 中,然后我们从 ConfigureServices 这个分支说起吧。
在 Startup 启动的时候,会在 ConfigureServices 中注册 AddMvc 的 DI 服务,那么MVC也是在这个时候注入到DI容器中的,在MVC中所有的注入都是使用 TryAddXXX 的形式,也就是如果容器中已经有相关服务的话,将不会添加新注册的服务,所以如果你有一些服务需要进行重写的话,需要在 builder.AddMvc() 之前注册到DI中。
我们先看一下 AddMvc 的返回值 IMvcBuilder , IMvcBuilder 是一个针对 IServiceCollection 包装的一个接口,除了IServiceCollection之外,还有一个 ApplicationPartManager 。那么它是干嘛的呢?
从命名来看 ApplicationPartManager 是用来管理 ApplicationPart 的,那么其实除了里面 ApplicationPart 之外还有 IApplicationFeatureProvider。
ApplicationPart
它是 MVC Core 中引用的一个抽象的概念,它允许你暴露一些特性或者一些已知的资源,比如一些元数据信息,发布的资源,磁盘的文件等。
你可以在应用程序启动的时候进行 ApplicationPart 的配置,它是作为 IMvcBuilder 扩展的一部分。
目前 MVC 框架针对 ApplicationPart 的默认实现只有 AssemblyPart,当然你可以根据需要进行扩展。
ApplicationPartManager
private static void AddDefaultFrameworkParts(ApplicationPartManager partManager) { var mvcTagHelpersAssembly = typeof(InputTagHelper).GetTypeInfo().Assembly; if(!partManager.ApplicationParts.OfType<AssemblyPart>().Any(p => p.Assembly == mvcTagHelpersAssembly)) { partManager.ApplicationParts.Add(new AssemblyPart(mvcTagHelpersAssembly)); } var mvcRazorAssembly = typeof(UrlResolutionTagHelper).GetTypeInfo().Assembly; if(!partManager.ApplicationParts.OfType<AssemblyPart>().Any(p => p.Assembly == mvcRazorAssembly)) { partManager.ApplicationParts.Add(new AssemblyPart(mvcRazorAssembly)); } }提供了从 IApplicationFeatureProvider 列表初始化 ApplicationPart 的功能,例如 FeatureProviders 可以是 ControllerFeatureProvider ,它可以通过从 ApplicationPart 列表中的暴露出来的类型来找到哪些是 Controller,并且把这些 Controller 添加到 ControllerFeature 中,那么在应用程序中这些被保存的 Controller 就将被视为控制器。
其实就是在 MVC 框架启动的时候,首先会把 Assembly 程序集转换为 ApplicationPart 添加到 ApplicationPartManager 对象列表中,才能执行后续的任务,因为要从这些程序集中查找 Controller,那么从这个特性我们可以延伸到, 利用此功能,我们可以从 Web 层剥离 Controller 到其他程序集中。
MVC 加载程序集主要是依靠 DefaultAssemblyPartDiscoveryProvider 这个类提供的功能,它是一个静态类位于 Microsoft.AspNetCore.Mvc.Internal 命名空间,有需要的同学可以直接使用。
下面是 AddMvc 的内部函数列表,它将每一个模块相关的服务封装成了一个 AddXXX 的拓展函数。
我们依次来看一下这些注册到 Services 中 AddXXX 内部都又注册了哪些东西。
AddMvcCore根据命名我们可以看到,它是MVC的核心服务,下面是一张图,罗列出了 MvcCore 内部注册的一些注册的接口,我们可以看到,有非常的多。
这个图注册的服务,比较多,我就不一一说明了,有几个关键的服务还是要说一下的。
Action相关:
先是 Action 相关的一些服务,可以说他们承载了 Mvc Core 中的核心,:
IActionDescriptorCollectionProvider: ActionDescriptorCollection 的提供程序,在内部他会对 Action 进行整理,然后我们可以在他的属性 ActionDescriptors 读取到相关信息。
ActionDescriptor:这个可能大家比较熟悉,它封装了Action上下文的很多信息,也就是Action的描述符。
ApplicationModel :MVC 应用程序的一个实体,包含了 Controller 信息,Filters信息,属性信息等,框架根据 ApplicationModel 存储的这些信息来构建(Build) ControllerActionDescriptor 列表。
ControllerActionDescriptor: 一个继承自 ActionDescriptor的类,新增了一些关于 ControllerName, ActionName ,MethodInfo 等属性。
IActionSelector:我们知道,当一个路由到我们MVC系统的时候,有可能这个路由会匹配到多个Action与其相符合,那么如何选着最合适的路由处理程序呢? 这个接口主要封装了相关逻辑。
还有 IControllerActivator ,IControllerActivator ,IActionInvokerFactory 我们将在下一篇中介绍。
然后是一些 Infrastructure 的一些服务,主要是一些Request 和 Response 流的处理,还有 xxxResult 的一些执行程序。
路由相关:
MvcRouteHandler, MvcAttributeRouteHandler 这两个我们留到下一篇介绍。
模型绑定验证相关:
IModelMetadataProvider:从方法类型的元数据中获取信息。
IModelBinderFactory:模型绑定工厂。
IObjectModelValidator:模型验证。
关于模型绑定和验证,后面我应该会单独有一篇博客介绍细节。
AddViewsViews 注入的服务主要包括,视图引擎、Html Helper 相关、Json Helper、View Components、CookieTempData、Antiforgery 等。
AddRazorViewEngine