NET/ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)

阅读目录:

  • 1.开篇介绍
  • 2.ASP.NETMVC Controller 控制器的入口(Controller的执行流程)
  • 3.ASP.NETMVC Controller 控制器的入口(Controller的继承体系)
  • 4.ASP.NETMVC IController Factory 控制器工厂(Controller的创建)

1】开篇介绍

经过前一篇文章.NET/ASP.NET Routing路由(深入解析路由系统架构原理) 的讲解,我们对ASP.NETRouting路由系统的整个运行机制有了一个基本的了解;当我们能清楚的知道Url是如何被解析成RouteData对象时,下面就是这些路由数据是如何被后面的应用框架所使用的,而通往应用框架的入口是MvcRouteHandler对象;

这篇文章将继续讲解通过路由后的ASP.NETMVC Controller控制器是如何被加载、激活并且执行的;跟控制器相关的一套对象模型是被MvcHandler对象作为源头调用起来的,也就是说,当我们穿过UrlRoutingModule对象后,并且成功的获取到应用框架配置的路由数据后,下面将进入IHttpHandler接口,而这个接口真是我们初始化RouteData对象时设定的应用框架入口,ASP.NETMVC所使用的是MvcHandler对象;

MvcRouteHandler对象是UrlRoutingModuleMvcHandler对象的连接器,只有MvcRouteHandler对象能成功执行后,方能进入到MvcHandler对象中,后续的一切运转才能顺利执行;

2】ASP.NETMVC Controller 控制器的入口(Controller的执行流程)

在系统刚启动的时候,也就是在Global.asax.cs文件里面我们配置了Http客户端请求服务器的Url模板;在路由解析模块(UrlRoutingModule)里面,它将通过字符串级别的操作,解析出我们Url模板中的{Controller}/{Action}等的占位符变量;所以这个时候Controller的概念对我们来说还只是一个字符串而已,而到了目前的这个Controller控制器解析的位置其实已经和路由基本没关系了,因为我们穿过了路由模块到达了Controller解析的环节;Controller解析已经属于ASP.NETMVC应用框架的范围,我们可以简单的将路由解析(UrlRoutingModule)的过程视为将请求的Url(含有数据的Url)与我们配置的Url模板进行模式匹配的过程,得出匹配后的Url数据(RouteData),然后将Url数据并且连同当前请求上下文一起封装成RequestContext对象(RouteData、HttpContextBase)传入到Controller解析的环节,也就是MvcHandler中,作为MvcHandler构造函数的参数;

当MvcHandler接管控制权之后它需要准备好对Controller的解析和执行,但是Controller并发一个简单的对象,它有一个复杂的继承体系和使用方式,原因在于它需要协调多方面的工作所以变的有很复杂;

根据MVC的架构模式理论便知道Controller是协调Model与View的中间纽带,它既要管理好Model的执行,也要管理好View的呈现;而原本MVC的架构模式提出的背景是在WinFrom的情况下,也就是传统C/S结构的系统;WinFrom结构的系统有一个好处就是它的执行很方便,从View的展现收集数据到Controller的调度执行Model会容易完成,但是ASP.NETMVC是建立在ASP.NET WEB背景之下的MVC模式框架,所以这个时候对Controller的激活会变的相当麻烦,因为在传输过程中Controller已经是字符串形式,如果是在C/S结构中那么Controller对于每次处理一样的View不会每次都进行激活;既然每次都需要激活就需要进行缓存策略,缓存策略只是Controller中的一个关键点,需要明白的是Controller的确需要做很多事情;

图1:

根据上图的执行顺序,能看出Controller控制器扮演着一个很重要的角色,所有的执行、返回值、视图呈现均需要通过它来管理调度;当然本章的重点是搞清楚此图中的第一环节,Controller是如何被加载激活的,这里面将涉及到众多的辅助对象模型,比如:ControllerFactory控制器工厂,而控制器工厂又将借助ControllerTypeCache来缓存Controller对象,而ConrollerTypeCache又将借助TypeCacheSerializer来对Controller缓存文件的序列化;

3.ASP.NETMVC Controller 控制器的入口(Controller的继承体系)

Controller控制器既然扮演着重要的角色,那么它就不会是一个简单的对象结构,它有着一个复杂的继承体系和对象模型支撑它来完成这些艰巨的任务;Controller要想能够运行起来,就需要搞清楚它有哪些执行入口,而需要知道它有哪些执行入口我们就需要搞清楚它的继承体系;入口的最高层抽象在哪一层,这样我们才能举一反三的扩展Controller的众多重要的功能;

首先我们了解到Controller的顶层抽象是IController接口,然后接着是ControllerBase抽象类实现了这个接口,而作为顶层抽象的实现ControllerBase完成了从IController接口继承下来的方法;


1

2

3

public interface IController {

    void Execute(RequestContext requestContext);

}

通过该代码段可以看出,Controller的执行需要一个RequestContext对象,而这个对象真是UrlRoutingModule环节所完成的结果,RequestContext对象内部封装了在Request阶段所获得的请求数据,里面包括了跟Http相关的请求上下文(HttpContextBase),最重要的是路由数据对象(RouteData);而控制器的执行必须需要RouteData中的有关Controller数据对象,也就是从请求Url中通过模式匹配出来的{Controller}部分的字符串;

ControllerBase定义了Controller使用到的部分公共属性,比如:用来保存临时数据的TempData,用来返回到View中的Model数据对象ViewBag、ViewData;并且初始化了ControllerContext对象,用来作为后续Controller使用的数据容器和操作上下文;


1

2

3

protected virtual void Initialize(RequestContext requestContext) {

    ControllerContext = new ControllerContext(requestContext, this);

}

在ControllerBase中将对IController.Execute(RequestContext requestContext)方法调用转到了protected abstract void ExecuteCore()方法中;这是一个典型的模板方法模式,下面的继承类Controller,只需要接着protected abstract void ExecuteCore()方法就能和ControllerBase衔接上;


1

public abstract class Controller : ControllerBase

Controller类继承自ControllerBase,而Controller的任务只需要完成ExecuteCore()方法;


1

2

3

4

5

6

7

8

9

10

11

12

protected override void ExecuteCore() {

           PossiblyLoadTempData();

           try {

              string actionName = RouteData.GetRequiredString("action");

               if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {

                   HandleUnknownAction(actionName);

               }

           }

           finally {

               PossiblySaveTempData();

           }

       }

Controller.ExecuteCore()的代码将从RouteData中获取执行action的名称,然后通过一个ActionInvoke的组件进行Action的调用,当Action被执行的时候将进入到我们继承的Controller,如:HomeController:Controller中,在我们自定的Controller中的方法都将被视为Action的匹配目标之一;

图2:

根据上图的指示,ControllerBase首先是实现IController接口,完成了对Execute(RequestContext requestContext)方法的实现,然后Controller继承ControllerBase类,重写了模板方法ExecuteCore()方法,然后我们自定义的HomeController其实是Action的容器,当Controller的ExecuteCore()方法执行时将通过ActionInvoke类进行对HomeController中的方法调用;

4.ASP.NETMVC IController Factory 控制器工厂(Controller的创建)

当清楚了Controller的继承体系之后,下面回到MvcHandler调用的环节;MvcHandler继承自IHttpHandler接口 ,表示它将是ASP.NET真正执行请求处理的地方;在MvcHandler处理请求的方法中ProcessRequest(HttpContextBase httpContext),将通过IControllerFactory接口创建IController接口;

IControllerFactory接口是控制器工厂接口,专门用来实现创建IController对象工厂类,在ASP.NETMVC内部有一个实现了IControllerFactory接口的默认工厂类DefaultControllerFactory,ASP.NETMVC内部是用这个类来创建IController对象的;


1

factory = ControllerBuilder.GetControllerFactory();

获取IDefaultControllerFactory接口需要通过ControllerBuilder对象,ControllerBuilder类是专门用来管理IControllerFactory对象的,同时ControllerBuilder也是应用编程接口,让自定义IControllerFactory对象成为可能;

创建IController需要我们传入RequestContext对象和ControllerName控制器名称;


1

2

3

4

// Get the controller type

string controllerName = RequestContext.RouteData.GetRequiredString("controller");

factory = ControllerBuilder.GetControllerFactory();

controller = factory.CreateController(RequestContext, controllerName);

从RequestContext.RouteData中获取到当前请求的conroller名称,然后用来作为factory.CreateController的参数;

图3:

MvcHandler通过ControllerBuilder对象的静态属性Current获取到ControllerBuilder对象实例,显然ControllerBuilder是一个单例模式的对象;然后通过ControllerBuilder对象获取到DefaultControllerFactory默认IControllerFactory工厂对象,接着利用DefaultControllerFactory创建出IController对象;

作者:王清培

出处:http://wangqingpei557.blog.51cto.com/

本文版权归作者和51CTO共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

时间: 2024-12-21 22:26:56

NET/ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)的相关文章

.Net Framework 4.5.1 ASP.NET MVC 5 下新建视图报“错误 运行所选代码生成器时出错 无法检索元数据 没有为该对象定义无参数构造函数”

当在控制器中新建视图的时候,选择的视图界面如下: 执行添加后报如下错误: 错误的内容为: 错误运行所选代码生成器时出错 无法检索"XXX"的元数据没有为该对象定义无参数构造函数 Unable to retrieve metadata for 'XXX'. No parameterless constructor defined for this object. 这个错误一直误导我,以为是模型类的问题,找了半天找不到解决方法,郁闷到死,后来发现是数据上下文的问题. 想到解决方案使用的是D

ASP.Net MVC Controller(控制器)

Controller主要负责响应用户的输入.主要关注的是应用程序流,输入数据的处理,以及对相关视图(View)输出数据的提供. 继承自:System.Web.Mvc.Controller 一个Controller可以包含多个Action. 每一个Action都是一个方法, 返回一个ActionResult实例 一个Controller对应一个xxController.cs控制文件,对应在View中有一个xx文件夹.一般情况一个Action对应一个View页面 Controller Action方

ASP.NET MVC Controller的激活

最近抽空看了一下ASP.NET MVC的部分源码,顺带写篇文章做个笔记以便日后查看. 在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,因此,说起Controller的激活,首先要从MvcHandler入手,MvcHandler实现了三个接口:IHttpAsyncHandler, IHttpHandler, IRequiresSessionState. 其处理逻辑主要实现在同步和异步的ProcessRequest方法中,总的来说,该方法在执行的时候,大致经历

ASP.NET MVC——Controller

Controller在ASP.NET MVC中负责控制所有客户端与服务器端的交互,并且负责协调Model与View之间的数据传递,是ASP.NET MVC的核心. 撰写Controller的基本要求: 1.Controller必须为公开类别: 2.Controller名称必须以Controller结尾: 3.必须继承ASP.NET MVC内建的Controller类别,或继承有实作IController界面的自定义类别,或自行实作IController: 4.所有动作方法必须为公开方法,任何非公

ASP.NET MVC Controller 编程所涉及到的常用属性成员

Controller (System.Web.Mvc.Controller) 1.获取路由中的各个值 Request.RequestContext.RouteData.Values["id"] 1).参考:System.Web.Mvc.Controller > Controller类 > 它的属性 2.相关参考:  html.ViewContext.RouteData.Values["controller"] , this HtmlHelper html

ASP.NET MVC 5 Web编程2 -- URL映射(路由原理)

本章将讲述ASP.NET MVC5 的路由原理,即URL映射机制. 简单点就是解释:为什么MVC在浏览器输入地址就能访问到类(或类中的方法)?这是怎么做到的?我自己可以通过.NET写出一个自己的MVC框架吗? 答案是:可以. 模拟URL映射 先来看一个Demo,在传统的.NET WebForms项目中,实现URL的拦截. 打开VS2013,新建一个“ASP.NET Web窗体应用程序”项目,并取名为Demo4URLRouting. 为了方便测试,注释掉Default.aspx页面的内容和模板引用

ASP.NET MVC自定义路由 - 实现IRouteConstraint限制控制器名(转载)

自定义约束前 namespace MvcApplication2 { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //默认 routes.MapRoute( name: "Default", url: "{controller}/

ASP.NET MVC Controller向View传值的几种方式

1)ViewBag ViewBag是动态类型,使用时直接添加属性赋值即可 ViewBag.myName 控制器代码: 1 public ActionResult Index() 2 { 3 ViewBag.name = "梁顺盛"; 4 ViewBag.message = "欢迎使用MVC设计模式~~"; 5 return View(); 6 } 视图代码: <div> <!--利用HtmlHelper创建TextBox时,使用名称与ViewBag

asp.net mvc Controller Factory

此文摘要小妞之路,记录一下,方便自己查看学习 Controller Factory控制器工厂: 一般在实际项目中的用法:使用内置的Controller Factory,叫 DefaultControllerFactory. 当 DefaultControllerFactory 类接收到一个 controller 实例的请求时,在 DefaultControllerFactory 类内部通过 GetControllerType 方法来获得 controller 的类型,然后把这个类型传递给 Get