如何构建ASP.NET Core MVC应用模型蓝图?

摘要:我个人觉得这是ASP.NET Core MVC框架体系最核心的部分。原因很简单,MVC框架建立在ASP.NET Core路由终结点上,它最终的目的就是将每个Action方法映射为一个或者多个路由终结点,路由终结点根据附加在Action上的若
我个人觉得这是ASP.NET Core MVC框架体系最核心的部分。原因很简单,MVC框架建立在ASP.NET Core路由终结点上,它最终的目的就是将每个Action方法映射为一个或者多个路由终结点,路由终结点根据附加在Action上的若干元数据构建而成。为了构建描述当前应用所有Action的元数据,MVC框架会提取出定义在当前应用范围内的所有Controller类型,并进一步构建出基于Controller的应用模型。应用模型不仅仅是构建Action元数据的基础,承载API的应用还可以利用它自动生成API开发文档,一些工具甚至可以利用应用模型自动生成消费API的客户端代码。这篇文章大概是两年之前写的,可能一些技术细节在最新版本的ASP.NET Core MVC已经发生了改变,但总体设计依然如此。 不论是面向Controller的MVC编程模型,还是面向页面的Razor Pages编程模型,客户端请求访问的目标都是某个Action,所以MVC框架的核心功能就是将请求路由到正确的Action,并通过执行目标Action的方式完成请求当前请求的处理。目标Action应该如何执行由描述它的元数据来决定,而这样的元数据是通过ApplicationModel类型标识的应用模型构建出来的。应用模型为MVC应用构建了一个基于Controller的蓝图,我们先从宏观的角度来看看这张蓝图是如何绘制的。 一、 总体设计 二、ApplicationModel 三、IApplicationModelProvider 四、IApplicationModelConvention 五、其他约定 六、ApplicationModelFactory 一、 总体设计图1基本体现了MVC框架构建应用模型的总体设计。代表使用模型的ApplicationModel对象是通过作为工厂的ApplicationModelFactory对象构建的,但是具体的构建任务却落在注册的一系列IApplicationModelProvider和IApplicationModelConvention对象上。 图1 ApplicationModel的构建模型 具体来说,ApplicationModelFactory工程会先创建一个空的ApplicationModel对象,并利用注册的IApplicationModelProvider对象对这个对象进行完善和修正。在此之后,代表默认约定的一系列IApplicationModelConvention对象会依次被执行,它们会将针对应用模型的约定规则应用到同一个ApplicationModel对象上。经过这两个加工环节之后得到的ApplicationModel最终成为描述应用模型的蓝图。 二、ApplicationModel表示应用模型的ApplicationModel对象不仅是常见Action元数据的依据,同时还有其他重要的用途。由于ApplicationModel对象绘制了整个应用的蓝图,我们经常不仅可以利用它来生成结构化API文档(比如Swagger),还可以利用它提供的元数据生成调用API的客户端代码。通过ApplicationModel表示的应用模型总体上具有如图2所示的结构:一个ApplicationModel对象包含多个描述Controller的ControllerModel对象,一个ControllerModel包含多个ActionModel和PropertyModel对象,ActionModel和PropertyModel是对定义在Controller类型中的Action方法和属性的描述。表示Action方法的ActionModel对象利用ParameterModel描述其参数。 图2 应用模型总体结构 三、IApplicationModelProvider在软件设计中我们经常会遇到这样的场景:我们需要构建一个由若干不同元素组成的复合对象,不同的组成元素具有不同的构建方式,MVC框架几乎基于采用了同一种模式来处理这样的场景。举个简单的例子:对象Foo需要实现的功能需要委托一组Bar对象来实现。MVC框架针对这种需求大都采用如图3所示模式来实现:Foo先创建一个上下文,并提供必要的输入,然后驱动每个Bar对象在这个上下文中完成各自的处理任务。所有Bar对象针对数据和状态的修改,以及产生的输出均体现在这个共享的上下文中,所有对象最终通过这个上下文就可以得到应有的状态或者所需的输出。 图3 基于共享上下文的多对象协作模式(单操作) 有时候我们甚至可以将Bar对象的操作分成两个步骤进行,比如我们将针对这两个步骤的操作分别命名为Executing和Executed。
阅读全文