剖析System.Web.Routing

ASP.NET MVC(以下简称mvc)的其中一个特性是使用了一个新的路由组件(routing engine)来提供一种更为舒适的将URL映射到程序中的特定页面上。在mvc开发的早期微软就意识到System.Web.Routing这个基础组件不但只为mvc使用,还应该能使用在传统的asp.net模型中,以提供更简单的URL重写功能(当然微软还意识到可以把它与Dynamic Data配合使用)。因此,他们把Routing这个功能从mvc中提取出来,并且作为.net 3.5 sp1的一部分发布.

那我们来看看它的工作原理吧!

System.Web.Routing有两个核心部分:RouteRoute Handler。一个route是一个简单的类,包含与请求的url想匹配的模式(pattern)。每个传入的url将会与你定义的Routes集合相匹配,只要匹配上第一个就会立刻使用该模式。一个Route看起来会像这样:

"Catalog/{Category}/{ProductId}" 

这个模式将会匹配任何传入的以”/Catalog/”开头的url,比如“/Catalog/Computers/3344”就会与该模式匹配。在花括号中的字符串叫做段(segment),这些将会被记录并且在之后的route handler中使用。这些route被定义在System.Web.Routing.RouteTable类的Routes这个静态字段中,在global.asax的Application_Start方法中是这样:
void   Application_Start( object   sender,   EventArgs   e)
{
   
RegisterRoutes(RouteTable.Routes) ;
}

public   static   void   RegisterRoutes(RouteCollection   routes)
{
   
routes.Add( new   Route( "Catalog/{Category}/{ProductId}" ,   new   CatalogRouteHandler())) ;
}
到这里,您可能注意到了RegisterRoutes方法中 CatalogRouteHandler这个类, 为了处理传入的请求去对应我们提供的route,我们需要创建他。一个程序可以有N个这样的Route handler去处理不同类型的请求。
所有的Route Handlers都是实现了只拥有一个叫做GetHttpHandler方法的IRouteHandler接口,这个方法返回一个IHttpHandler。这个接口大家应该非常熟悉了,他也只有一个方法——带有HttpContext类型参数的ProcessRequest方法。
我们刚刚从提供的一系列模式(pattern)中去匹配了一系列url,当找到了匹配的模式后我们将使用轩昂关联的IRouteHandler去获取将要能够回应这个请求的IHttpHandler。
我们的例子IRouteHandler将要返回一个Page类的实例。上文中的GetHttpHandler方法带有一个RequestContext类型的参数。RequestContext类有2个属性:一个是类型为System.Web.HttpContextBase的HttpContext,另一个是System.Web.Routing.RouteData类型的RouteData。
System.Web.HttpContextBase是.net 3.5 sp1中增加的一个类,他是一个对以前的不方便做测试的HttpContext类的abstract wrapper。需要注意到是他属于System.Web.Abstractions程序集。所以要引用它才可以。
HttpContext属性只允许访问我们从HttpContext中收集到的信息,因此我们可以根据请求的数据自身来判断使用的route。比如如果我们想要对于http和https的请求做不同的操作,或者我们需要redirect到其他域,或者是接受从子域(sub-domain)传递过来的请求。
RouteData属性存储了所有与我们定义的route和段(segements)的数据。他有一个RouteBase类型的Route属性,他存储的是该请求所对应的route。其次,他还有一个Values属性包含有段信息的数据。比如我们请求的”/Catalog/Computers/3444”将会使Values属性是这样:
routeData.Values[ "Category" ] == "Computers"
routeData.Values[ "ProductId" ] == "3444"
然后我们就可以把这些值通过HttpContext的tem属性传递到页面上。比如:
public   class   CatalogRouteHandler   :   IRouteHandler  
{  
   
public   IHttpHandler   GetHttpHandler(RequestContext   requestContext)  
   
{              
        
foreach   (KeyValuePair< string ,   object >   token   in   requestContext.RouteData.Values)  
        
{                  
            
requestContext. HttpContext .Items.Add(token.Key,   token.Value) ;  
        
}              
        
IHttpHandler   result   =   BuildManager  
            
.CreateInstanceFromVirtualPath( "~/Product.aspx" ,   typeof (Product))   as   IHttpHandler ;  
        
return   result ;  
   
}  
}  
接下来我们看一下routes的一些其他特性:第一个就是我们可以为routes设置默认值,比如当“Catalog/{Category}/{ProductId}”中没有ProductId段时ProductId段的默认值为0001,或者Category段没有提供时默认为Default。这个非常简单,是一个Route类构造函数的简单的overload。这部分代码大致是这样:

private
  static   void   RegisterRoutes(RouteCollection   routes)  
{  
   
routes.Add( new   Route( "Catalog/{Category}/{ProductId}" ,  
       
new   RouteValueDictionary( new   {   Category   = "Default" ,   ProductId   = "0001" }),  
       
new   CatalogRouteHandler())) ;  
}  

就像你看到的一样,我们创建了一个RouteValueDictionary,并且使用默认值复制给属性来初始化它。这个过程也可以通过使用集合初始化来完成:

private   static   void   RegisterRoutes(RouteCollection   routes)  
{                                     
   
routes.Add( new   Route( "Catalog/{Category}/{ProductId}" ,  
       
new   RouteValueDictionary   {   { "Category" , "Default" },   { "ProductId" , "0001" }   },    
       
new   CatalogRouteHandler())) ;              
}  
现在我们看一下route约束(route constraints),约束有多种格式。比如,如果我们限制ProductId段最多由4个数字组成,那么我们可以这样实现这个约束:
routes.Add( new   Route( "Catalog/{Category}/{ProductId}" ,  
   
new   RouteValueDictionary   {   { "Category" , "Default" },   { "ProductId" , "0001" }   },  
   
new   RouteValueDictionary   {   { "ProductId" , @"/d{1,4}" }   },  
   
new   CatalogRouteHandler())) ;    
上面这个例子我们可以看到另一个只有一个ProductId项的RouteValueDictionary。当ProductId为5个数字的时候就不会匹配该route了。约束也可以是实现IRouteConstraint接口的形式,该接口有一个Match方法,传递所有与request有关的信息到类里,并且允许为route约束创建的自定义的逻辑传递到类中,是一个很强大的东东。有一个已经内建的HttpMethodConstraint允许你限制你的route区别一个给定的http verb,比如get或post。下面的代码就限制了只有get的请求才会匹配这个route:
routes.Add( new   Route( "Catalog/{Category}/{ProductId}" ,  
   
new   RouteValueDictionary   {   { "Category" , "Default" },   { "ProductId" , "0001" }   },  
   
new   RouteValueDictionary   {   { "ProductId" , @"/d{1,4}" },   { "httpMethod" ,   new   HttpMethodConstraint( "get" )}   },  
   
new   CatalogRouteHandler())) ;
这里我们只是在httpMethod上做了约束,其实你可以任意调用你需要的。HttpMethodConstraint只是使用一系列的http verbs作为构造函数然后做检查。
工作原理基本就介绍完了,这里再提一个类:StopRoutingHandler。从名字就能看出来是用来停用一个route的。这个需要放在我们提供的所有route定义的顶部(因为匹配原则):
routes.Add( new   Route( "{service}.asmx/{*path}" ,   new   StopRoutingHandler())) ;  

如果有一大堆的route的话,这个东东可是非常实用的。

但是这东西怎么进入asp.net的管线(pipeline)的?当然是需要HttpModule了。System.Web.Routing.UrlRoutingModule就是实现IHttpModule接口的HttpModule,他插入request pipeline然后中断asp.net框架对url的处理,而让routing去处理,如此以来routing就开始掌权了。

web.config里需要这样设置:

< add   name = "UrlRoutingModule" type = "System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />  

如果使用的是iis7,在handlers节里插入:

< add   name = "UrlRoutingHandler" preCondition = "integratedMode" verb = "*" path = "UrlRouting.axd" type = "System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />


OK,现在对routing的机制了解了吧:)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值