THis is test for 页首

Asp.net Mvc 请求是如何到达 MvcHandler的——UrlRoutingModule、MvcRouteHandler分析,并造个轮子

前言

  本文假定读者对 HttpModule 、HttpHandler和IIS的处理流程有一定的了解,如果为了解可以参考以下链接。文中大部分代码通过Reflector反编译  System.Web.dll 得到,.net 版本为4.0 

IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述

IIS 7.0 的 ASP.NET 应用程序生命周期概述

HTTP 处理程序和 HTTP 模块概述

   Asp.net MVC 程序虽然开发的模式不同,但是其本质上还是 Asp.net。其利用了HttpModule 和 HttpHandler 做了扩展,可以参考博客园里的大牛——Artech 相关系列文章。

本文主要关注UrlRoutingModule 、MvcRouteHandler 两个类的源代码,进而分析客户的请求是如何到达MvcHandler 的。

Asp.net MVc 程序启动流程 需要关注的行为

  • 1、Application启动时先通过RouteTable把URL映射到Handler
  • 2、通过UrlRouting Module 这个HttpModule 拦截用户请求。

我们知道,HttpModule 是注册在 Web.config 中的,可是当你打开Asp.net MVc 程序的Web .Config 时 却没有发现该配置节,原因是:"它已经默认的写在全局的中"。应此 你可以在 “$\Windows\Microsoft.NET\Framework\版本号\Config\Web.config“ 中找到 " <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />”

UrlRoutingModule 源码


UrlRoutingModule 位于 System.web.dll 文件中,利用Reflector 可以查看到其源码:

UrlRoutingModuel
 1 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
 2 public class UrlRoutingModule : IHttpModule
 3 {
 4     // Fields
 5     private static readonly object _contextKey = new object();
 6     private static readonly object _requestDataKey = new object();
 7     private RouteCollection _routeCollection;
 8  
 9     // Methods
10     protected virtual void Dispose()
11     {
12     }
13  
14     protected virtual void Init(HttpApplication application)
15     {
16         if (application.Context.Items[_contextKey] == null)
17         {
18             application.Context.Items[_contextKey] = _contextKey;
19             application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
20         }
21     }
22  
23     private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
24     {
25         HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
26         this.PostResolveRequestCache(context);
27     }
28  
29     [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
30     public virtual void PostMapRequestHandler(HttpContextBase context)
31     {
32     }
33  
34     public virtual void PostResolveRequestCache(HttpContextBase context)
35     {
36         RouteData routeData = this.RouteCollection.GetRouteData(context);
37         if (routeData != null)
38         {
39             IRouteHandler routeHandler = routeData.RouteHandler;
40             if (routeHandler == null)
41             {
42                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
43             }
44             if (!(routeHandler is StopRoutingHandler))
45             {
46                 RequestContext requestContext = new RequestContext(context, routeData);
47                 context.Request.RequestContext = requestContext;
48                 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
49                 if (httpHandler == null)
50                 {
51                     throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
52                 }
53                 if (httpHandler is UrlAuthFailureHandler)
54                 {
55                     if (!FormsAuthenticationModule.FormsAuthRequired)
56                     {
57                         throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
58                     }
59                     UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
60                 }
61                 else
62                 {
63                     context.RemapHandler(httpHandler);
64                 }
65             }
66         }
67     }
68  
69     void IHttpModule.Dispose()
70     {
71         this.Dispose();
72     }
73  
74     void IHttpModule.Init(HttpApplication application)
75     {
76         this.Init(application);
77     }
78  
79     // Properties
80     public RouteCollection RouteCollection
81     {
82         get
83         {
84             if (this._routeCollection == null)
85             {
86                 this._routeCollection = RouteTable.Routes;
87             }
88             return this._routeCollection;
89         }
90         set
91         {
92             this._routeCollection = value;
93         }
94     }
95 }

 

UrlHttpModule 实现了 IHttpModule 接口

  HTTP Module在应用程序发出请求时被调用的并进行特定的事件处理。 HTTP Module作为请求管道的一部分调用,它们能够访问请求过程中请求周期中的各种管线事件。
Http Module必须经过注册才能从请求管道接收通知。 注册 HTTP 模块的最常用方法是在应用程序的 Web.config 文件中进行注册。 在 IIS 7.0 中,统一的请求管道使您还可以通过其他方式注册模块,其中包括通过 IIS 管理器和 Appcmd.exe 命令行工具。
  当 ASP.NET 创建表示您的应用程序的 HttpApplication 类的实例时,将创建已注册的任何模块的实例。 在创建模块时,将调用它的 Init 方法,并且模块会自行初始化。在模块的 Init 方法中,可以注册各种应用程序事件的处理程序(如 BeginRequest 或 EndRequest)。

可以看到 UrlHttpModule 在 init 方法中注册了PostResolveRequestCache 事件的处理程序。
关于Asp.net的生命周期事件可以参考:
http://msdn.microsoft.com/zh-cn/library/ms178472
http://msdn.microsoft.com/zh-cn/library/bb470252
http://msdn.microsoft.com/zh-cn/library/ms178473


PostResolveRequestCache

    该事件在完成缓存解析并投递时触发。

  在UrlRoutingModule中它主要是进行上下文的初始化,同时根据传递过来的路由信息获取指定IHttpHandler (其实就是我们的MvcHandler类)
最后通过 context.RemapHandler() 代码将HttpHandler 处理程序映射到 管线处理中。
在 PostResolveRequestCache 之前分别触发的事件有:
引发 BeginRequest 事件。
引发 AuthenticateRequest 事件。
引发 PostAuthenticateRequest 事件。
引发 AuthorizeRequest 事件。
引发 PostAuthorizeRequest 事件。
引发 ResolveRequestCache 事件。

http://i.msdn.microsoft.com/dynimg/IC5405.png


核心逻辑代码:

 1 //获取路由信息
 2 RouteData routeData = this.RouteCollection.GetRouteData(context);
 3 IRouteHandler routeHandler = routeData.RouteHandler;
 4 //构建请求上下文
 5 RequestContext requestContext = new RequestContext(context, routeData);
 6 context.Request.RequestContext = requestContext;
 7 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
 8 //将MvcHandler 实例 映射到管线中(通常我们是利用web.config 进行配置的,但是MvcHandler 没有默认无参构造函数,所以直接通过向其传递一个实例
 9 //进行映射)
10 context.RemapHandler(httpHandler);

如何获取到MvcRouteHandler?

  MvcRouteHandler 实现了 IRouteHandler接口。 上文的 IRouteHandler routeHandler=routeData.RouteHandler; 在Asp.net MVc 程序中实际上获取的是MvcRouteHandler实例。
RouteData 类中包含了 IRouteHandler实例的引用,它通过 RouteData 的构造函数:

public RouteData(RouteBase route, IRouteHandler routeHandler);

或者 属性

public IRouteHandler RouteHandler { get; set; }

进行注入。

我们再往回搜索,RouteData实例是通过 RouteCollection.GetRouteData(Context) 方法获取的。查看该方法的主要逻辑实现:

 using (this.GetReadLock())
        {
            foreach (RouteBase base2 in this)
            {
                RouteData routeData = base2.GetRouteData(httpContext);
                if (routeData != null)
                {
                    return routeData;
                }
            }
        }

可以看到通过 RouteBase 类的 GetRouteData(HttpContext)获取了 RouteData实例,并且将第一个部位Null的值返回。 我们需要深入查看RouteBase GetRouteData方法。 RouteBase 是抽象类,其方法是在 Route上具体实现的。(这里又引出一个问题,程序是何时将 Route实例绑定到了 RouteBase上)。

Route类 

深入到Route 类中 发现其和 RouteData 一样, IRouteHandler 也是通过 构造参数 或 属性对 IRouteHandler 进行了注入。

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
    RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
    if (values == null)
    {
        return null;
    }
    RouteData data = new RouteData(this, this.RouteHandler);
    if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
    {
        return null;
    }
    foreach (KeyValuePair<string, object> pair in values)
    {
        data.Values.Add(pair.Key, pair.Value);
    }
    if (this.DataTokens != null)
    {
        foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
        {
            data.DataTokens[pair2.Key] = pair2.Value;
        }
    }
    return data;
}

关于Asp.net Mvc 中的 MapRoute() 方法

  在Asp.net MVc 程序Global 文件的RegisterRoutes 方法里,RouteCollection 类使用的是MapRoute 方法添加的路由,该方法是一个扩展方法。它位于System.Web.Mvc 的RouteCollectionExtensions类中。

 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;
        }

 

查看上面的关键行:
Route route = new Route(url, new MvcRouteHandler()) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};

 

可以清楚看到,当我们在Asp.net MVc 程序里使用MapRoute()添加路由时,会生成一个 Route 类的实例,并且该实例在生成时会被注入 MvcRouteHandler 实例。 最后被添加到 RouteCollection的集合里(Route 和 MvcRouteHandler 都是以多态形式存在于 RouteCollection中的),这样就建立了一个关系映射表,只有请求的上下文通过该上下文的验证,就可以返回对应MvcRouteHandler实例。

  • MvcRouteHandler 实现了 IRouteHandler接口
  • Route 继承了 RouteBase 抽象类。
  • RouteCollection 中维护着一个 RouteBase 集合。

如何根据路由信息获取MvcHandler

  在Asp.net Mvc 程序 启动时,会触发 Appliction_start 方法,该方法调用了 RouteTable,生成一个全局RouteCollection(单件模式),并将其作为参数传递到RegisterRoutes 方法中。

registerRoutes 方法里通过MapRoute 向 RouteCollection添加路由。 添加的路由是包含MvcRouteHandler实例的Route。


当用户的请求到达IIS后,由于Asp.net MVC 注册了一个HttpModule(UrlRoutingModule) ,应此会触发对应的管线事件处理方法。


1、根据上下文获取RouteData方法, 其内部 实现是:从RouteCollection中遍历RouteBase(实际为Route)实例,并调用RouteBase的GetRouteData() 方法,如果上下文与路由匹配
该方法就会构造一个 RouteData实例,并将 this.IRouteHandler的实例 注入(在Application映射路由时,每个Route实例都包含一个MvcRouteHandler实例的引用)。


2、从 RouteData中 获取IRouteHandler 实例,即 MvcRouteHandler


3、构建上下文,创建RequestContext 该类仅包含 HttpContextBase 和 RouteData 。调用MvcHandler的构造函数需要传递该参数(可以知道请求的Controller、action名等信息),MvcHandler 实现了 IHttpHandler,注意它与MvcHttpHandler 是不相同的:

MvcHandler . 此处理程序负责启动用于 MVC 应用程序的 ASP.NET 管道。 它从 MVC 控制器工厂接收 Controller 实例;此控制器处理请求的进一步处理。 注意,即使 MvcHandler 实现 IHttpHandler,也不能将其映射为处理程序(例如,.mvc 文件扩展名),因为该类不支持无参数构造函数。 (它唯一的构造函数需要一个 RequestContext 对象。)

因此 在 UrlRoutenModule 中 是 通过 HttpContext.RemapHttp(HttpHandler) 。 直接将 一个实例 映射到处理程序上 。(不需要通过系统对其实例化)。

MvcHttpHandler . 此处理程序用于在不通过路由模块的情况下帮助直接处理程序映射。 如果您希望一个文件的扩展名(如 .mvc)直接映射到一个 MVC 处理程序,这很有用。 在内部,MvcHttpHandler 执行 ASP.NET 路由通常执行的相同任务(通过 MvcRouteHandler 和 MvcHandler)。 但是,它将这些任务作为处理程序而不是模块来执行。 UrlRoutingModule 为所有请求启用时,通常不使用此处理程序。



4、调用IRouteHandler的 GetHttpHandler 方法 获取 IHttpHandler实例。(即调用了MvcRouteHandler实例的GetHttpHandler 方法,生成了一个 MvcHandler 实例)


5、向当前上下文注册 IHttpHandler 实例,进入 Controller 处理。

 

 经过以上步骤,我们就大致了解到了Asp.net Mvc程序启动后,用户的请求是如何到达HttpHandler的。


RouteTable 类

这个类很简单只包含一个静态的RouteCollection 属性,是一个单件类。

RouteTable

在Asp.net MVc 中application_start 方法里 调用了RouteTable来获取唯一的RouteCollection实例,

所以在UrlRouteModuel中可以通过RouteTable.Routes获取所配置的路由集合。

RouteCollection类

  是一个集合类,内部维护着一个RouteBase 以路由名作为Key的字典集合, 所以我们可以给路由命名。主要的属性和方法有:

RouteData GetRouteData(HttpContextBase httpContext)

  该方法遍历集合内部的RouteBase实体,并返回第一个非Null的RouteData ,具体的RouteData实例,是由所遍历的RouteBase 通过调用 RouteBase.GetRouteData(HttpContext)方法获取的 。

返回的RouteData 中包含一个 IRouteHandler 对象,该接口的只有一个方法,GetHttpHandler,用于获取IHttpHandler 对象。

VirtualPathData GetVirtualPath(...)

  该方法具有多个重载,当您使用 ASP.NET 路由框架生成 URL 时,GetVirtualPath 方法将返回 VirtualPathData 类的一个实例。 VirtualPathData 类包含与所提供的上下文匹配路由的相关信息。

Route MapPageRoute(...)

  该方法用于向路由集合中添加路由,提供此方法是为了方便编码, 它等效于调用 Add 方法。其内部实现的主要代码为:

Route item = new Route(routeUrl, defaults, constraints, dataTokens, new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));

注册使用 PageRouteHandler 类创建的 Route 对象。

  在Asp.net Mvc 方法中,并没有利用该方法向集合中添加路由而是通过了了 MapRoute()方法 这是一个扩展方法,定义在了 RouteCollectionExtensions 类中。

static Route MapRoute(this RouteCollection routes, ......)

  这是一个扩展方法,并且具有多个重载,目的是方便编码,用于向RouteCollection中 添加路由。其内部主要是 生成了一个 以 MvcRouteHandler 为 路由处理的Route 并将其加入到集合中。

Route route = new Route(url, new MvcRouteHandler()) {
                Defaults = new RouteValueDictionary(defaults),
                Constraints = new RouteValueDictionary(constraints),
                DataTokens = new RouteValueDictionary()
            };

IRouteHandler 接口

  接口的定义很简单,只有一个 GetHttpHandler 方法用于返回 一个 IHttpHandler 对象。在 Route 对象,及 RouteData对象中 都包含该对象的引用。该接口定义了一种协议, 指定了 Route 即路由应该有哪一个处理对象进行处理。 它是 Route 到 Handler的 重要桥梁。

RouteBase 类

   这是一个抽象类,RouteBase 类用于定义应用程序中的路由。 在定义路由时,通常使用 Route 类,Route 类是从 RouteBase 类派生的。 但是,如果要提供与 Route 类所提供的功能不同的功能,则可以创建一个从 RouteBase 派生的类,并实现所需的属性和方法。

主要方法有:GetRouteData 在派生类中重写时,会返回有关请求的路由信息。
GetVirtualPath 在派生类中重写时,会检查路由是否与指定值匹配,如果匹配,则生成一个 URL,然后检索有关该路由的信息。

Route 类

  可以通过 Route 类指定 ASP.NET 应用程序中路由的处理方式。 可以为要映射的每个 URL 模式创建一个 Route 对象,该类可处理与该模式相对应的请求。 当应用程序收到请求时,ASP.NET 路由会循环访问 Routes 集合中的路由,以查找与该 URL 模式匹配的第一个路由。

  可将 Url 属性设置为 URL 模式。 该 URL 模式包含某些分段,这些分段位于 HTTP 请求中应用程序名称之后。 例如,在 URL http://www.contoso.com/products/show/beverages 中,该模式应用于 products/show/beverages。 包含三个分段的模式(如 {controller}/{action}/{id})与 URL http://www.contoso.com/products/show/beverages 匹配。 每个分段由 / 字符分隔。 如果分段位于大括号({ 和 })内,则表明该分段是一个 URL 参数。 ASP.NET 路由会检索请求中的值并将其分配给 URL 参数。 在上面的示例中,URL 参数 action 被赋予值 show。 如果该分段不在大括号内,则该值被视为文本值。将 Defaults 属性设置为 RouteValueDictionary 对象,该对象包含当 URL 中缺少某个参数时所使用的值,或者包含用于设置 URL 中未参数化的其他值的值。 将 Constraints 属性设置为 RouteValueDictionary 对象,该对象包含的值为正则表达式或 IRouteConstraint 对象。 这些值用于确定参数值是否有效。

  

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
    RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
    if (values == null)
    {
        return null;
    }
    RouteData data = new RouteData(this, this.RouteHandler);
    if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
    {
        return null;
    }
    foreach (KeyValuePair<string, object> pair in values)
    {
        data.Values.Add(pair.Key, pair.Value);
    }
    if (this.DataTokens != null)
    {
        foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
        {
            data.DataTokens[pair2.Key] = pair2.Value;
        }
    }
    return data;
}

该方法重写了父类的方法,返回一个RouteData 作。方法一开始 首先通过 ParsedRoute 类的 Match 方法进行路由匹配,匹配成功后则生成一个RouteData对象实例。

ParsedRoute 类
该类是一个内部类,用于匹配、绑定URL,大家可以参考:

 

 public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
    BoundUrl url = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints);
    if (url == null)
    {
        return null;
    }
    if (!this.ProcessConstraints(requestContext.HttpContext, url.Values, RouteDirection.UrlGeneration))
    {
        return null;
    }
    VirtualPathData data = new VirtualPathData(this, url.Url);
    if (this.DataTokens != null)
    {
        foreach (KeyValuePair<string, object> pair in this.DataTokens)
        {
            data.DataTokens[pair.Key] = pair.Value;
        }
    }
    return data;
}

  该方法返回与路由相关联的URL信息。其内部是通过ParsedRoute 类的Bind 把RouteData 绑定到一个BoundUrl 对象中。 如果有约束在则进行验证,最后返回一个 VirtualPathData 对象。

 

protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
    object obj2;
    IRouteConstraint constraint2 = constraint as IRouteConstraint;
    if (constraint2 != null)
    {
        return constraint2.Match(httpContext, this, parameterName, values, routeDirection);
    }
    string str = constraint as string;
    if (str == null)
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[] { parameterName, this.Url }));
    }
    values.TryGetValue(parameterName, out obj2);
    string input = Convert.ToString(obj2, CultureInfo.InvariantCulture);
    string pattern = "^(" + str + ")$";
    return Regex.IsMatch(input, pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase);
}

  这是一个路由约束的处理方法,可以看到如果传递的 object constraint 是一个 IRouteConstraint 类型 则直接调用 其Match 方法。

如果正则表达式字符串,利用正则表达式进行验证,否则抛出异常。 


 

造一个简陋的轮子

  不要再造轮子是软件设计的一个准则,但是在学习研究时,造一个简单的轮子能更好的帮我们了解其原理。

创建一个HttpHandler 类,让Asp.net程序通过路由,运行我们指定的HttpHandler,同时在Handler中能够获取路由信息。

1、创建一个类库项目,在项目里创建一个WheelHandler类并实现IHttpHandler接口,这个类只有一个构造函数构造函数需要传递一个 RequestContext

 public class WheelHandler : IHttpHandler
    {
        public WheelHandler(RequestContext requestContext)
        {

            this.RequestContext = requestContext;
        }

        #region IHttpHandler Members

        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write(String.Format("this is a Wheel for {0}Controller and {1}action "
                , this.RequestContext.RouteData.Values["Controller"]
                , this.RequestContext.RouteData.Values["Action"]));
            context.Response.End();
        }

        #endregion

        public RequestContext RequestContext { get; private set; }
    }

 WheelHandler没有默认的无参构造函数,所以不能直接在Web.config 中注册。ProcessRequest对象很简单,就是输出传入的路由信息。

我们需要定义一个IRouteHandler对象,当路由被捕获时,返回一个WheelHandler,然后将其映射到Http处理中。

2.定义WheelRouteHandler

 public class WheelRouteHandler : IRouteHandler
    {


        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new WheelHandler(requestContext);
        }

        IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
        {
            return this.GetHttpHandler(requestContext);
        }
    }

3、映射路由和WheelRouteHandler。

  我们可以自定义一个派生自RouteBase 的类,进行特定的路由处理,但是本例是一个简单的"轮子",因此继续使用Route类,只需要在生成Route时 向其注入 WheelRouteHandler即可。

因此我们需要修改添加路由的方式,这里模仿MVc 利用扩展方法。当然你也可以不用扩展方法,在添加路由是直接使用Add方法,记得注入WheelRouteHandler便行。

public static Route MapWheelRoute(this RouteCollection routes, string name, string url, object defaults)
        {
            return MapWheelRoute(routes, name, url, defaults, null, null);
        }

        public static Route MapWheelRoute(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 与 WheelRouteHandler的映射关系
            Route route = new Route(url, new WheelRouteHandler())
            {
                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;
        }

 

MapWheelRoute 方法里 调用了Route构造函数,并传入一个WheelRouteHandler对象。

4、创建HttpModule 对象

  自定义的HttpModule的责任是,构建上下文,创建HttpHandler对象并将它映射到Http处理程序里去。

 public void Init(HttpApplication context)
        {
            context.PostResolveRequestCache += new EventHandler(context_PostResolveRequestCache);
        }

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

        }

        private void PostResolveRequestCache(HttpContextBase context)
        {
            RouteData routeData = RouteTable.Routes.GetRouteData(context);

            if (routeData == null)
            {
                throw new InvalidOperationException();
            }

            IRouteHandler routeHandler = routeData.RouteHandler;
            if (routeHandler == null)
            {
                throw new InvalidOperationException();
            }

            RequestContext requestContext = new RequestContext(context, routeData);
            IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
            if (httpHandler == null)
            {
                throw new InvalidOperationException("无法创建对应的HttpHandler对象");
            }
            context.RemapHandler(httpHandler);

        }

  这里我们也是模仿Mvc 捕获的是PostResolveRequestCache事件进行处理,处理时首先将 包装HttpContext对象为 HttpContextBase对象。然后通过GetRouteData 获取RouteData对象,它包含有IRouteHandler对象。获取到IRouteHandler对象后,需要构造 一个RequestContext 对象(该对象很简单就是包含上下文和路由信息)因为 创建WheelHandler 需要该对象。

创建好IHttpHandler对象后,利用RemapHandler方法将其映射为处理程序。

5、注册路由 和Module

  新建一个空的Asp.net 项目,移除里面所有的Aspx文件,事实上只要保留Global和 Web.Config文件即可。在Global 里注册路由:

路由注册
 1   void Application_Start(object sender, EventArgs e)
 2         {
 3             RegRoutes(RouteTable.Routes);
 4         }
 5 
 6         private void RegRoutes(RouteCollection routeCollection)
 7         {
 8             routeCollection.MapWheelRoute(null,
 9                "{controller}/{action}/{*id}",
10                new { controller = "Home", action = "index", id = 1 });
11         }

  完成最后我们还需要组成自定义的HttpModule

在Web.Config 中添加如下配置:

 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="WheelRouting" type="WheelRouting.WheelRoutingModule,WheelRouting"/>
    </modules>

  </system.webServer>

关于 HttpModule的介绍 可以参考:演练:创建和注册自定义 HTTP 模块

下面是运行结果:

Controller默认值为 Home  Action 默认值为 index

 

源码:点击下载

 

 

posted @ 2012-08-11 22:51  DotDot  阅读(10841)  评论(15编辑  收藏  举报
页脚