在第一篇博文中我已经具体的介绍了asp.net WebForm的传统编程方式的核心流程,由此可以知道asp.net是基于管道(即由20个事件组成,其中19个事件向程序员公开)设计的,因此具有很好的扩展性。而mvc的框架正是基于asp.net的良好的扩展性来实现。稍微理解asp.net核心的人都应该清楚,asp.net的扩展点主要体现在过滤器HttpMoudler和处理器HttpHandler这两个核心的组件。正是基于自定义的这两个组件来实现了mvc的框架。
如何对asp.net进行扩展呢?我们知道在asp.net的webForm与asp.net的mvc的编程中有一个很大的区别在于mvc方式客户端请求的不再是传统的webForm编程方式下的某一个具体的存在你服务器磁盘上的物理文件,而是一个Controller下的具体的Action。而我们知道在asp.net的事件管道中在 EventPostMapRequestHandler事件中就已经创建了Http请求的处理器对象HttpHandler,该处理器对象实现了IHttpHandler接口。在WebForm中创建的IHttpHandler对象是页面类Page的处理器,在System.Web.UI.Page命名空间下。而实际上对于不同的资源类型来说创建的HttpHandler是不一样的。那在mvc的模式下,对应的HttpHandler对象就是MvcHandler,我们来具体看看他是如何被创建的。
这其中还涉及到asp.net的另外一个核心组件HttpModule,所有的处理器类都实现了IHttpModule接口,顾名思义就是过滤器,即对Http请求进行拦截过滤,有关于这两个核心组件我将会用另外的一篇文章来阐述,这里暂且不详解。在mvc中UrlRoutingModule实现了IHttpModule接口,我们知道系统会调用所有实现了IHttpModule的HttpModule中的Init 方法来注册管道中事件的订阅,该方法具体实现如下:
void IHttpModule.Init(HttpApplication application);
该方法定义如下:
protected virtual void Init(HttpApplication application)
{
if (application.Context.Items[_contextKey] == null)
{
application.Context.Items[_contextKey] = _contextKey;
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
}
}
|
在Init事件中注册了PostResolveRequestCache事件,该事件定义如下:
我们来看看IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);中得到的httpHandler到底是什么?通过以上代码我们可以知道,得到的httpHandler是由类RouteData的属性RouteHanler得到的,我们来看看RouteData类中RouteHanler属性的定义:
private IRouteHandler _routeHandler; public IRouteHandler RouteHandler { get {return this._routeHandler;} set {this._routeHandler = value;} } |
由属性RouteHanler的定义我们可以知道,调用的GetHttpHandler方法是定义在接口IHttpHandler,我们来看具体的定义:
public interface IRouteHandler { IHttpHandler GetHttpHandler(RequestContext requestContext); } |
而MvcRouteHandler实现了该接口,但是微软实现GetHttpHandler方法我们需要注意一下:
public class MvcRouteHandler : IRouteHandler { .................................. protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext)); return new MvcHandler(requestContext); } #region IRouteHandler Members IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) { return GetHttpHandler(requestContext); } #endregion } |
GetHttpHandler方法简单粗暴,就是直接返回一个MvcHandler对象。既然找到了这个mvc的核心组件MvcHandler,那他是如何被注册到事件管道中的呢?我们来看PostResolveRequestCache事件的最后一句话:context.RemapHandler(httpHandler);知道了这一句代码在什么执行的话,我们就知道了他是如何注册到管道中的。
在PostResolveRequestCache事件中传入的context是HttpContextBase的实例,具体实现如下:
|
而HttpContextWrapper的方法实现如下:
|
因此我们知道context最终返回是HttpContext对象。因此我们来看看HttpContext类里面的RemapHandler方法的实现:
public void RemapHandler(IHttpHandler handler) { this.EnsureHasNotTransitionedToWebSocket(); IIS7WorkerRequest request = this._wr as IIS7WorkerRequest; if (request != null) { if (this._notificationContext.CurrentNotification >= RequestNotification.MapRequestHandler) { throw new InvalidOperationException(System.Web.SR.GetString("Invoke_before_pipeline_event", new object[] { "HttpContext.RemapHandler", "HttpApplication.MapRequestHandler" })); } string handlerType = null; string handlerName = null; if (handler != null) { Type type = handler.GetType(); handlerType = type.AssemblyQualifiedName; handlerName = type.FullName; } request.SetRemapHandler(handlerType, handlerName); } this._remapHandler = handler; } |
初始化该_remapHandler之后,我们可以注意到HttpContext有一个RemapHandlerInstance属性,该属性直接返回了初始化了之后的_remapHandler对象:
internal IHttpHandler RemapHandlerInstance { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get { return this._remapHandler; } } |
那接下来我们弄清楚这个HttpContext对象的RemapHandlerInstance属性什么时候被调用了,这个要对asp.net的管道熟悉的才会想到,在HttpApplication内部类MaterializeHandlerExecutionStep中:
|
这个内部类中的void HttpApplication.IExecutionStep.Execute();该方法中涉及到的具体代码如下,红色代码初始化了RemapHandlerInstance属性:
void HttpApplication.IExecutionStep.Execute() .............................................................................................................(此处省略) |
那MaterializeHandlerExecutionStep这个方法又是在哪里被调用了呢?在HttpApplication的内部类 PipelineStepManager的BuildSteps被调用:
通过以上源码分析,我们知道在传统的WebForm中的管道中在事件PostMapRequestHandler就获得了页面类的HttpHandler对象,在这个事件之后再执行一些列的事件和经过页面生命周期,将处理的结果返回给客户端。因此要在此基础上不再创建具体的页面类对象的话,就必须在PostMapRequestHandler事件之前创建MvcHandler对象而不是页面类的HttpHandler对象。因此在第7个事件PostResolveRequestCache中将创建好的MvcHandler对象注册到管道中,从而实现了mvc的框架。