ASP.NET MVC请求处理管道生命周期的19个关键环节(13-19)
在上一篇"ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12) ",体验了7-12关键环节,本篇继续。
⒀当请求到达UrlRoutingModule的时候,UrlRoutingModule取出请求中的Controller、Action等RouteData信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler,即MVCRouteHandler

MVCRouteHandler是用来生成实现IHttpHandler接口的MvcHandler:
namespace System.Web.Routing
{
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
}
UrlRoutingModule如何把请求交给MVCRouteHandler?
通过分析UrlRoutingModule的源码可以看到:
//通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例
RouteData routeData = this.RouteCollection.GetRouteData(context);
//再从RouteData中获取MVCRouteHandler
IRouteHandler routeHandler = routeData.RouteHandler;
为什么可以从RouteData中拿到MVCRouteHadnler呢?
因为当我们在HttpApplication的第一个管道事件,使用MapRoute()方法注册路由的时候,已经通过Route类的构造函数把MVCRouteHandler注入到路由中了。
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
if (routes == null) {
throw new ArgumentNullException("routes");
}
if (url == null) {
throw new ArgumentNullException("url");
}
Route route = new Route(url, new MvcRouteHandler()) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
if ((namespaces != null) && (namespaces.Length > )) {
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}
⒁MVCRouteHandler把请求交给MvcHandler
还是从UrlRoutingModule的源码可以看到,通过HttpHandler的GetHttpHandler()方法获取到了实现了IHttpHandler接口的MVCHandler:
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
context.RemapHandler(httpHandler);

MvcHandler的部分源码为:
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
{
protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
SecurityUtil.ProcessInApplicationTrust(() =>
{
IController controller;
IControllerFactory factory;
ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory
try
{
controller.Execute(RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
});
} private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current);
if (isRequestValidationEnabled == true) {
ValidationUtility.EnableDynamicValidation(HttpContext.Current);
}
AddVersionHeader(httpContext);
RemoveOptionalRoutingParameters();
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);
if (controller == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName));
}
}
}
⒂从以上可以看出:首先通过ControllerBuilder的静态方法GetControllerFactory获取到实现IControllerFactory接口的ControllerFactory,然后根据从上下文中的路由数据中拿到controller名称,并据此创建实现IController接口的Controller

Controller派生于ControllerBase, 而ControllerBase实现了IController接口。ControllerBase的部分源码如下:
public abstract class ControllerBase : IController
{
protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException(
MvcResources.ControllerBase_CannotExecuteWithNullHttpContext,
"requestContext");
}
VerifyExecuteCalledOnce();
Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
ExecuteCore();
}
}
protected abstract void ExecuteCore();
......
}
从中可以看成:
● 每次调用controller,都会执行基类ControllerBase的Execute()方法
● Execute()方法又会调用ExecuteCore()这个抽象方法
● ExecuteCore()这个抽象方法的实现被定义在Controller中
● 在Controller中的ExecuteCore()方法会调用ActionInvoker的InvokeAction()方法
⒃ActionInvoker激发Action方法

ActionInvoker实现了IActionInvoker接口:
public interface IActionInvoker
{
bool InvokeAction(ControllerContext controllerContext, string actionName);
}
MVC默认的ActionInvoker是ControllerActionInvoker。
在Controller类中,提供了类型为IActionInvoker的属性ActionInvoker,当执行ExecuteCore()方法时会让这个ActionInvoker调用InvokeAction()方法激发Action。如下:
public class Controller
{
......
private IActionInvoker _actionInvoker;
public IActionInvoker ActionInvoker
{
get
{
if(_actionInvoker == null)
{
_actionInvoker = CreateActionInvoker();
}
return _actionInvoker;
}
set
{
_actionInvoker = value;
}
} protected virtual IActionInvoker CreateActionInvoker()
{
return new ControllerActionInvoker();
} public override void ExecuteCore()
{
ActionInvoker.InvokeAction(...);
}
.....
}
ActionInvoker在执行InvokeAction()方法时会需要有关Controller和Action的相关信息,实际上,Controller信息(比如Controller的名称、类型、包含的Action等)被封装在ControllerDescriptor这个类中,Action信息(比如Action的名称、参数、属性、过滤器等)被封装在ActionDescriptor中。
另外,ActionDescriptor还提供了一个FindAction()方法,用来找到需要被执行的Action。
⒄ActionInvoker在执行InvokeAction()方法返回ActionResult

ActionResult是一个抽象类:
public abstract class ActionResult
{
public abstract void ExecuteResult(ControllerContext context);
}
如果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。
public abstract class ViewResultBase : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (String.IsNullOrEmpty(ViewName))
{
ViewName = context.RouteData.GetRequiredString("action");
} ViewEngineResult result = null; if (View == null)
{
//通过视图引擎获取到ViewEngineResult ,此时模板页面【aspx】被加载成了WebViewPage<TModel>
result = FindView(context);
View = result.View;
} TextWriter writer = context.HttpContext.Response.Output;
ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
View.Render(viewContext, writer); if (result != null)
{
result.ViewEngine.ReleaseView(context, View);
}
}
}
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)的更多相关文章
- ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6)
ASP.NET和ASP.NET MVC的HttpApplication请求处理管道有共同的部分和不同之处,本系列将体验ASP.NET MVC请求处理管道生命周期的19个关键环节. ①以IIS6.0为例 ...
- ASP.NET MVC请求处理管道生命周期的19个关键环节(7-12)
在上一篇"ASP.NET MVC请求处理管道生命周期的19个关键环节(1-6) ",体验了1-6关键环节,本篇继续. ⑦根据IsapiWorkerRequest对象,HttpRun ...
- Asp.net MVC 之请求生命周期
今天主要试着描述一下ASP.NET MVC 请求从开始到结束的整个生命周期,了解这些后,对MVC会有一个整体的认识. 这里主要研究了MVC请求的五个过程. 1.创建RouteTable 当ASP.NE ...
- ASP.NET MVC 小牛之旅4:ASP.NET MVC的运行生命周期
ASP.NET MVC的运行生命周期大致分成三大过程:(1)网址路由对比. (2)运行Controller与Action. (3)运行View并回传结果. 4.1网址路由对比 当iis收到http请求 ...
- 你真的熟悉ASP.NET MVC的整个生命周期吗?
一.介绍 我们做开发的,尤其是做微软技术栈的,有一个方向是跳不过去的,那就是MVC开发.我相信大家,做ASP.NET MVC 开发有的有很长时间,当然,也有刚进入这个行业的.无论如何,如果有人问你,你 ...
- 详解ASP.NET MVC的请求生命周期
本文的目的旨在详细描述asp.net mvc请求从开始到结束的每一个过程. 我希望能理解在浏览器输入url并敲击回车来请求一个asp.net mvc网站的页面之后发生的任何事情. 为什么需要关心这些? ...
- ASP.NET MVC的请求生命周期
我希望能理解在浏览器输入URL并敲击回车来请求一个ASP.NET MVC网站的页面之后发生的任何事情. 为什么需要关心这些?有两个原因.首先是因为ASP.NET MVC是一个扩展性非常强的框架.例如, ...
- ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程
好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人>: --> 开篇:上一篇 ...
- ASP.Net MVC请求处理流程
ASP.Net MVC请求处理流程 好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人& ...
随机推荐
- Maven学习总结(二)——Maven项目构建过程练习
上一篇只是简单介绍了一下maven入门的一些相关知识,这一篇主要是体验一下Maven高度自动化构建项目的过程 一.创建Maven项目 1.1.建立Hello项目 1.首先建立Hello项目,同时建立M ...
- bind的用处
刚做的项目,遇到过这样的问题,就是在动态追加标签时,给追加的标签添加事件时,在标签内追加不了,后来使用了delegate代理,能响应了,但也是不能给动态追加的代理 $("body" ...
- ca des key crt scr
openssl genrsa -des3 -out domain.key 1024 openssl req -new -key domain.key -out domain.csr openssl r ...
- java break语句的三种用法
1.用于switch语句当中,用于终止语句 2.用于跳出循环,此为不带标签的break语句,相当与goto的作用 e.g while(i<j&&h<k){ if(h< ...
- Python:生成器
生成器:使用了 yield 的函数被称为生成器(generator).它记住上一次返回时在函数体中的位置. yeild运行过程:当你向生成器要一个数时,生成器会执行,直至出现 yield 语句,生成器 ...
- hdu 5818 (优先队列) Joint Stacks
题目:这里 题意: 两个类似于栈的列表,栈a和栈b,n个操作,push a x表示把数x放进a栈的栈底,pop b 表示将栈b的栈顶元素取出输出,并释放这个栈顶元素,merge a b表示把后面的那个 ...
- [Shell]Bash变量:变量测试与内容替换
--------------------------------------------------------------------------------------------- 变量测试是通 ...
- spring 源码下载地址
1, github: https://github.com/spring-projects/spring-framework SVN: https://github.com/spring-projec ...
- ASP.NET反射(转载)
两个现实中的例子:1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况.这是如何做到的呢?B超是B型超声波,它可以透过肚皮通过向你体内发射B型超声波,当超声波遇到内脏壁的时 ...
- Yii中事件触发机制
控制器初始化中添加事件处理方法,在需要触发的地方直接触发 public function init() { parent::init(); // TODO: Change the autogenera ...