View Code

URL Routing组件是如何与ASP.NET MVC框架组合起来的

最近有一段时间都在看ASP.NET MVC框架的源代码。原来的想法是,如果有相应ASP.NET MVC的文档,就直接看文档,学学怎么使用这个框架。可惜都是视频教程,而又感觉看那本电子书有点无趣,索性就直接看源代码了。这样一来反而收获了很多的东西。

在ASP.NET MVC框架里面,用到了URL Routing组件。URL Routing组件干什么用呢?根据你配置的URL 模式来提取数据或者生成相应的URL 。但我有一个疑问:URL Routing组件是如何同ASP.NET MVC框架关联起来的?

由于这个组件没有开放源代码,所以我们查看代码的方式也就只有反编译了。我们可以看到Route类型。这个类表示一条路由,包括URL模式,路由数据,HTTP处理程序等。我们可以看到它的构造器如下:

 

public Route(string url, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);

 

 

Route的初始化时需要一个实现IRouteHandler接口的类的实例的。IRouteHandler的定义如下:
 
public interface IRouteHandler{
    IHttpHandler GetHttpHandler(RequestContext requestContext);
}

 

 

IRouteHandler只定义了一个方法——GetHttpHandler,返回一个IHttpHandler。IHttpHandler其实就是ASP.NET管线事件的终点,所有的HTTP请求最后都是由IHttpHandler的实现来处理的。

沿着这个思路,我们继续往下找,看能不能找到一个实现这个接口的类型。一开始看到一个UrlRoutingHandler,以为找到要找的类型了,不过仔细一开类型的定义,是抽象类型,表示Routing的终点,因为这个抽象类实现了IHttpHandler接口。查看它的ProcessRequest方法的代码,如下:

 

protected virtual void ProcessRequest(HttpContextBase httpContext)
{
    RouteData routeData = this.RouteCollection.GetRouteData(httpContext);
    if (routeData == null)
    {
        throw new HttpException(0x194, SR.GetString("UrlRoutingHandler_NoRouteMatches"));
    }
    IRouteHandler routeHandler = routeData.RouteHandler;
    if (routeHandler == null)
    {
        throw new InvalidOperationException(SR.GetString("UrlRoutingModule_NoRouteHandler"));
    }
    RequestContext requestContext = new RequestContext(httpContext, routeData);
    IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    if (httpHandler == null)
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
    }
    this.VerifyAndProcessRequest(httpHandler, httpContext);
}

 

 

可以看到,实际上这个方法首先使用Route的实例返回一个IRouteHandler接口实现类的实例对象,调用IRouteHandler.GetHttpHandler()方法返回一个IHttpHandler实现类的实例对象,最后调用了this.VerifyAndProcessRequest(httpHandler, httpContext)方法进行处理。VerifyAndProcessRequest(httpHandler, httpContext)定义如下:
 
protected abstract void VerifyAndProcessRequest(
  IHttpHandler httpHandler, 
  HttpContextBase httpContext);

 

这是一个抽象方法。URL Routing组件最后一定映射到一个处理程序来相应HTTP请求,显然,必定有一个实现类继承并且实现了这个VerifyAndProcessRequest方法。我们在这个组件里面接着找,还是没有找到相应的实现类型,但是我们发现了一个UrlRoutingModule类。这个类实现了IHttpModule,是一个ASP.NET模块。那么这个模块做了什么?我们可以查看这个模块的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);
    }
}
 
它订阅了ASP.NET的PostResolveRequestCache管线事件,并且这个方法重新映射了HTTP 处理程序,其实这个类型在.NET 3.5以及.NET 4.0中还是有所不同的。(this.OnApplicationPostResolveRequestCache方法代码如下:
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.CurrentUICulture, 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);
            }
        }
    }
}
 

这个方法从Route实例取得RouteData,并且从RouteData获得相应的IHttpHandler处理程序来处理请求,这样,URL Routing最终把HTTP请求映射到一个处理程序处理。但是这里有个问题,就是这个处理程序的实例是什么时候初始化,又是如何初始化的?并且,URL Routing是怎么和ASP.NET MVC框架关联起来的?

 
带着这个问题去看ASP.NET MVC框架的源代码,我们找到两个引人注意的类型:MvcHttpHandler以及MvcRouteHandler。MvcHttpHandler继承了UrlRoutingHandler, IHttpAsyncHandler, IRequiresSessionState,是一个HttpHandler。看来这个类型并不是我们要寻找的答案。于是,我想到是不是利用配置文件来把这两个组件关联起来的?于是我去看了Oxite的配置文件,注意到有这么一段配置:
<httpHandlers>
  <remove verb="*" path="*.asmx"/>
  <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
  <add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpHandlers>
<httpModules>
  <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
这段配置还是没有解决我们的疑问。因为我们知道ASP.NET MVC框架必定在某一个地方创建了一个MvcHttpHandler的实例并且将这个实例传递给URL Routing组件,URL Routing最终将请求映射到这个处理程序,然后相应请求。联想到我们每一个路由都是有一个Route类型表示的,那么我们如何实例化这个类型?一般都是在Application_Start事件里初始化并且添加到路由表里面的吧?OK,也许问题的答案就在这个地方。我们添加路由的时候,记得都是用MapRoute这个拓展方法吧,这个方法非常方便,但是这个方法被定义在哪里?在找遍了URL Routing组件,我并没有发现有这样的这样的拓展方法。于是我想到可能又是放在了ASP.NET MVC框架里了。于是我在框架里发现了RouteCollectionExtensions这个类型,这个类型为RouteCollection定义了非常多的拓展方法,其中就包括MapRoute方法。在编写实现的时候,框架使用了串联的方式,也就是很多的重载方法,其实都是调用一个实现方法。这个实现方法如下:
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 > 0)) {
    route.DataTokens["Namespaces"] = namespaces;
  }

  routes.Add(name, route);

  return route;
}
 
在兜了一圈,终于回来了。我们非常清楚的看到,是在这里,把MvcHttpHandler注入,也就将URL Routing和ASP.NET MVC关联起来了。

总结

 
这里只是整理我在看ASP.NET MVC框架源代码中,忽然想到的框架是如可和URL Routing组件关联起来这个问题的解题思路,绕了一圈才忽然明白,因此我将我的思路记录了下来。其实一句话,在初始化Route类型的时候就将IHttpHandler实例注入,这样就完成了关联了。
posted on 2010-11-22 17:35  湘军投研  阅读(420)  评论(1编辑  收藏  举报