• 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实例注入,这样就完成了关联了。
  • 相关阅读:
    Windbg使用
    C#与C++之间类型的对应
    Hook CreateProcess
    基于EasyHook实现监控explorer资源管理器文件复制、删除、剪切等操作
    GUID和UUID、CLSID、IID 区别及联系
    hook C++
    EasyHook Creating a remote file monitor
    hook工具
    EasyHook
    Hook exe 和 file
  • 原文地址:https://www.cnblogs.com/jinacookies/p/1884590.html
Copyright © 2020-2023  润新知