mvc源码解读(1)-如何找到mvcHandler

     在第一篇博文中我已经具体的介绍了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事件,该事件定义如下:

public virtual void PostResolveRequestCache(HttpContextBase context)
{
    RouteData routeData = this.RouteCollection.GetRouteData(context);
    if (routeData != null)
    {
        IRouteHandler routeHandler = routeData.RouteHandler;
        if (routeHandler == null)
        {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
        }
        if (!(routeHandler is StopRoutingHandler))
        {
            RequestContext requestContext = new RequestContext(context, routeData);
            context.Request.RequestContext = requestContext;
            IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
            if (httpHandler == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
            }
            if (httpHandler is UrlAuthFailureHandler)
            {
                if (!FormsAuthenticationModule.FormsAuthRequired)
                {
                    throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
                }
                UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
            }
            else
            {
                context.RemapHandler(httpHandler);
            }
        }
    }
}

 我们来看看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的实例,具体实现如下:

private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
    HttpApplication application = (HttpApplication) sender;
    HttpContextBase context = new HttpContextWrapper(application.Context);
    this.PostResolveRequestCache(context);
}

 而HttpContextWrapper的方法实现如下:

public HttpContextWrapper(HttpContext httpContext)
{
    if (httpContext == null)
    {
        throw new ArgumentNullException("httpContext");
    }
    this._context = httpContext;
}

 因此我们知道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中:

internal class MaterializeHandlerExecutionStep : HttpApplication.IExecutionStep
{
    // Fields
    private HttpApplication _application;

    // Methods
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    internal MaterializeHandlerExecutionStep(HttpApplication app);
    void HttpApplication.IExecutionStep.Execute();

    // Properties
    bool HttpApplication.IExecutionStep.CompletedSynchronously { get; }
    bool HttpApplication.IExecutionStep.IsCancellable { get; }
}

 这个内部类中的void HttpApplication.IExecutionStep.Execute();该方法中涉及到的具体代码如下,红色代码初始化了RemapHandlerInstance属性:

void HttpApplication.IExecutionStep.Execute()
{
    HttpContext httpContext = this._application.Context;
    HttpRequest request = httpContext.Request;
    IHttpHandler handler = null;
    string managedHandlerType = null;
    if (EtwTrace.IsTraceEnabled(5, 1))
    {
        EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_ENTER, httpContext.WorkerRequest);
    }
    IIS7WorkerRequest workerRequest = httpContext.WorkerRequest as IIS7WorkerRequest;
    if (httpContext.RemapHandlerInstance != null)
    {
        workerRequest.SetScriptMapForRemapHandler();
        httpContext.Handler = httpContext.RemapHandlerInstance;
    }

.............................................................................................................(此处省略)

那MaterializeHandlerExecutionStep这个方法又是在哪里被调用了呢?在HttpApplication的内部类 PipelineStepManager的BuildSteps被调用:

internal override void BuildSteps(WaitCallback stepCallback)
{
    HttpApplication app = base._application;
    HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(app);
    app.AddEventMapping("ManagedPipelineHandler", RequestNotification.MapRequestHandler, false, step);
    app.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false, app.CreateImplicitAsyncPreloadExecutionStep());
    HttpApplication.IExecutionStep step2 = new HttpApplication.CallHandlerExecutionStep(app);
    app.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false, step2);
    HttpApplication.IExecutionStep step3 = new HttpApplication.TransitionToWebSocketsExecutionStep(app);
    app.AddEventMapping("ManagedPipelineHandler", RequestNotification.EndRequest, true, step3);
    HttpApplication.IExecutionStep step4 = new HttpApplication.CallFilterExecutionStep(app);
    app.AddEventMapping("AspNetFilterModule", RequestNotification.UpdateRequestCache, false, step4);
    app.AddEventMapping("AspNetFilterModule", RequestNotification.LogRequest, false, step4);
    this._resumeStepsWaitCallback = stepCallback;
}

     通过以上源码分析,我们知道在传统的WebForm中的管道中在事件PostMapRequestHandler就获得了页面类的HttpHandler对象,在这个事件之后再执行一些列的事件和经过页面生命周期,将处理的结果返回给客户端。因此要在此基础上不再创建具体的页面类对象的话,就必须在PostMapRequestHandler事件之前创建MvcHandler对象而不是页面类的HttpHandler对象。因此在第7个事件PostResolveRequestCache中将创建好的MvcHandler对象注册到管道中,从而实现了mvc的框架。

posted @ 2013-01-27 23:31  肖&申&克  阅读(490)  评论(0编辑  收藏  举报