在上一篇"ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12) ",体验了7-12关键环节,本篇继续。

⒀当请求到达UrlRoutingModule的时候,UrlRoutingModule取出请求中的Controller、Action等RouteData信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler,即MVCRouteHandler

MVCRouteHandler是用来生成实现IHttpHandler接口的MvcHandler:

  1. namespace System.Web.Routing
  2. {
  3. public interface IRouteHandler
  4. {
  5. IHttpHandler GetHttpHandler(RequestContext requestContext);
  6. }
  7. }

UrlRoutingModule如何把请求交给MVCRouteHandler?
通过分析UrlRoutingModule的源码可以看到:

//通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例
RouteData routeData = this.RouteCollection.GetRouteData(context);

//再从RouteData中获取MVCRouteHandler
IRouteHandler routeHandler = routeData.RouteHandler;

为什么可以从RouteData中拿到MVCRouteHadnler呢?
因为当我们在HttpApplication的第一个管道事件,使用MapRoute()方法注册路由的时候,已经通过Route类的构造函数把MVCRouteHandler注入到路由中了。

  1. public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
  2. if (routes == null) {
  3. throw new ArgumentNullException("routes");
  4. }
  5. if (url == null) {
  6. throw new ArgumentNullException("url");
  7. }
  8.  
  9. Route route = new Route(url, new MvcRouteHandler()) {
  10. Defaults = new RouteValueDictionary(defaults),
  11. Constraints = new RouteValueDictionary(constraints),
  12. DataTokens = new RouteValueDictionary()
  13. };
  14.  
  15. if ((namespaces != null) && (namespaces.Length > )) {
  16. route.DataTokens["Namespaces"] = namespaces;
  17. }
  18.  
  19. routes.Add(name, route);
  20.  
  21. return route;
  22. }

⒁MVCRouteHandler把请求交给MvcHandler

还是从UrlRoutingModule的源码可以看到,通过HttpHandler的GetHttpHandler()方法获取到了实现了IHttpHandler接口的MVCHandler:

  1. IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
  2. context.RemapHandler(httpHandler);

MvcHandler的部分源码为:

  1. public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
  2. {
  3. protected internal virtual void ProcessRequest(HttpContextBase httpContext)
  4. {
  5. SecurityUtil.ProcessInApplicationTrust(() =>
  6. {
  7. IController controller;
  8. IControllerFactory factory;
  9. ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory
  10. try
  11. {
  12. controller.Execute(RequestContext);
  13. }
  14. finally
  15. {
  16. factory.ReleaseController(controller);
  17. }
  18. });
  19. }
  20.  
  21. private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
  22. bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current);
  23. if (isRequestValidationEnabled == true) {
  24. ValidationUtility.EnableDynamicValidation(HttpContext.Current);
  25. }
  26. AddVersionHeader(httpContext);
  27. RemoveOptionalRoutingParameters();
  28. string controllerName = RequestContext.RouteData.GetRequiredString("controller");
  29. factory = ControllerBuilder.GetControllerFactory();
  30. controller = factory.CreateController(RequestContext, controllerName);
  31. if (controller == null) {
  32. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName));
  33. }
  34. }
  35. }

⒂从以上可以看出:首先通过ControllerBuilder的静态方法GetControllerFactory获取到实现IControllerFactory接口的ControllerFactory,然后根据从上下文中的路由数据中拿到controller名称,并据此创建实现IController接口的Controller

Controller派生于ControllerBase, 而ControllerBase实现了IController接口。ControllerBase的部分源码如下:

  1. public abstract class ControllerBase : IController
  2. {
  3. protected virtual void Execute(RequestContext requestContext)
  4. {
  5. if (requestContext == null)
  6. {
  7. throw new ArgumentNullException("requestContext");
  8. }
  9. if (requestContext.HttpContext == null)
  10. {
  11. throw new ArgumentException(
  12. MvcResources.ControllerBase_CannotExecuteWithNullHttpContext,
  13. "requestContext");
  14. }
  15. VerifyExecuteCalledOnce();
  16. Initialize(requestContext);
  17. using (ScopeStorage.CreateTransientScope())
  18. {
  19. ExecuteCore();
  20. }
  21. }
  22. protected abstract void ExecuteCore();
  23. ......
  24. }

从中可以看成:
● 每次调用controller,都会执行基类ControllerBase的Execute()方法
● Execute()方法又会调用ExecuteCore()这个抽象方法
● ExecuteCore()这个抽象方法的实现被定义在Controller中
● 在Controller中的ExecuteCore()方法会调用ActionInvoker的InvokeAction()方法

⒃ActionInvoker激发Action方法

ActionInvoker实现了IActionInvoker接口:

  1. public interface IActionInvoker
  2. {
  3. bool InvokeAction(ControllerContext controllerContext, string actionName);
  4. }

MVC默认的ActionInvoker是ControllerActionInvoker。

在Controller类中,提供了类型为IActionInvoker的属性ActionInvoker,当执行ExecuteCore()方法时会让这个ActionInvoker调用InvokeAction()方法激发Action。如下:

  1. public class Controller
  2. {
  3. ......
  4. private IActionInvoker _actionInvoker;
  5. public IActionInvoker ActionInvoker
  6. {
  7. get
  8. {
  9. if(_actionInvoker == null)
  10. {
  11. _actionInvoker = CreateActionInvoker();
  12. }
  13. return _actionInvoker;
  14. }
  15. set
  16. {
  17. _actionInvoker = value;
  18. }
  19. }
  20.  
  21. protected virtual IActionInvoker CreateActionInvoker()
  22. {
  23. return new ControllerActionInvoker();
  24. }
  25.  
  26. public override void ExecuteCore()
  27. {
  28. ActionInvoker.InvokeAction(...);
  29. }
  30. .....
  31. }

ActionInvoker在执行InvokeAction()方法时会需要有关Controller和Action的相关信息,实际上,Controller信息(比如Controller的名称、类型、包含的Action等)被封装在ControllerDescriptor这个类中,Action信息(比如Action的名称、参数、属性、过滤器等)被封装在ActionDescriptor中。

另外,ActionDescriptor还提供了一个FindAction()方法,用来找到需要被执行的Action。

⒄ActionInvoker在执行InvokeAction()方法返回ActionResult

ActionResult是一个抽象类:

  1. public abstract class ActionResult
  2. {
  3. public abstract void ExecuteResult(ControllerContext context);
  4. }

如果ActionResult是非ViewResult,比如JsonResult, ContentResult,这些内容将直接被输送到Response响应流中,显示给客户端;如果是ViewResult,就会进入下一个渲染视图环节。

⒅ViewEngine找到需要被渲染的视图

默认的有Razor View Engine和Web Form View Engine,实现IViewEngine接口。

IViewEngine接口方法:
● FindPartialView
● FindView
● ReleaseView

如果要创建自定义View Engine,只需要派生于VirtualPathProviderViewEngine这个类。

⒆View被加载成WebViewPage<TModel>类型,并渲染生成Html

调用ViewResult的ExecuteResult()方法,通过IView的Render()方法渲染成Html。

  1. public abstract class ViewResultBase : ActionResult
  2. {
  3. public override void ExecuteResult(ControllerContext context)
  4. {
  5. if (context == null)
  6. {
  7. throw new ArgumentNullException("context");
  8. }
  9. if (String.IsNullOrEmpty(ViewName))
  10. {
  11. ViewName = context.RouteData.GetRequiredString("action");
  12. }
  13.  
  14. ViewEngineResult result = null;
  15.  
  16. if (View == null)
  17. {
  18. //通过视图引擎获取到ViewEngineResult ,此时模板页面【aspx】被加载成了WebViewPage<TModel>
  19. result = FindView(context);
  20. View = result.View;
  21. }
  22.  
  23. TextWriter writer = context.HttpContext.Response.Output;
  24. ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
  25. View.Render(viewContext, writer);
  26.  
  27. if (result != null)
  28. {
  29. result.ViewEngine.ReleaseView(context, View);
  30. }
  31. }
  32. }

ASP.NET MVC请求处理管道生命周期的19个关键环节系列包括:

ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6)

ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12)

ASP.NET MVC请求处理管道生命周期的19个关键环节(13-19)

ASP.NET MVC请求处理管道生命周期的19个关键环节(13-19)的更多相关文章

  1. ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6)

    ASP.NET和ASP.NET MVC的HttpApplication请求处理管道有共同的部分和不同之处,本系列将体验ASP.NET MVC请求处理管道生命周期的19个关键环节. ①以IIS6.0为例 ...

  2. ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12)

    在上一篇"ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6) ",体验了1-6关键环节,本篇继续. ⑦根据IsapiWorkerRequest对象,HttpRun ...

  3. Asp.net MVC 之请求生命周期

    今天主要试着描述一下ASP.NET MVC 请求从开始到结束的整个生命周期,了解这些后,对MVC会有一个整体的认识. 这里主要研究了MVC请求的五个过程. 1.创建RouteTable 当ASP.NE ...

  4. ASP.NET MVC 小牛之旅4:ASP.NET MVC的运行生命周期

    ASP.NET MVC的运行生命周期大致分成三大过程:(1)网址路由对比. (2)运行Controller与Action. (3)运行View并回传结果. 4.1网址路由对比 当iis收到http请求 ...

  5. 你真的熟悉ASP.NET MVC的整个生命周期吗?

    一.介绍 我们做开发的,尤其是做微软技术栈的,有一个方向是跳不过去的,那就是MVC开发.我相信大家,做ASP.NET MVC 开发有的有很长时间,当然,也有刚进入这个行业的.无论如何,如果有人问你,你 ...

  6. 详解ASP.NET MVC的请求生命周期

    本文的目的旨在详细描述asp.net mvc请求从开始到结束的每一个过程. 我希望能理解在浏览器输入url并敲击回车来请求一个asp.net mvc网站的页面之后发生的任何事情. 为什么需要关心这些? ...

  7. ASP.NET MVC的请求生命周期

    我希望能理解在浏览器输入URL并敲击回车来请求一个ASP.NET MVC网站的页面之后发生的任何事情. 为什么需要关心这些?有两个原因.首先是因为ASP.NET MVC是一个扩展性非常强的框架.例如, ...

  8. ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程

    好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人>: --> 开篇:上一篇 ...

  9. ASP.Net MVC请求处理流程

    ASP.Net MVC请求处理流程 好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人& ...

随机推荐

  1. MySQL的表的优化和列类型的选择

    列选择原则: 1:字段类型优先级 整型 > date,time  >  enum,char>varchar > blob 列的特点分析: 整型: 定长,没有国家/地区之分,没有 ...

  2. gulp-htmlmin压缩html

    通过一条命令用Npm安装gulp-htmlmin: npm install gulp-htmlmin --save-dev 安装完毕后,打开gulpfile.js文件,我们里面编写一个task用来专门 ...

  3. UART UVM验证平台平台搭建总结

    tb_top是整个UVM验证平台的最顶层:tb_top中例化dut,提供时钟和复位信号,定义接口以及设置driver和monitor的virual interface,在intial中调用run_te ...

  4. Python 集合方法总结

    1.添加一个元素:    add(...) Addan element to a set. 1 2 3 4 >>> a = {'shaw',11,22} >>>a. ...

  5. c++学习笔记——字面值常量类

    字面值常量类:数据成员都是字面值类型的聚合类是字面值常量类.如果一个类不是聚合类,但是它符合一下要求,则它也是个字面值常量类: 1.数据成员都必须是字面值类型. 2.类必须至少含有一个constexp ...

  6. 7.Mybatis关联表查询(这里主要讲的是一对一和一对多的关联查询)

    在Mybatis中的管理表查询这里主要介绍的是一对一和一对多的关联查询的resultMap的管理配置查询,当然你也可以用包装类来实现.不过这里不说,做关联查询的步骤可以简单的总结为以下的几步: 1.分 ...

  7. Hosting static website on AWS

    http://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html#root-d ...

  8. Servlet实现重定向的两种方式

    使用Servlet实现请求重定向:两种方式 1. response.setStatus(302); response.setHeader("location", "/Re ...

  9. css2选择器

    CSS1&2元素选择器 选择符  类型  版本  简介 * 通配选择符 CSS2 所有元素对象. E 类型(HTML)选择符 CSS1 以文档语言对象类型作为选择符. E#myid id选择符 ...

  10. Hibernate个人学习笔记(2)

    新增改查的操作 一.cfg.xml配置 <?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration ...