System.Web.Routing - TOMMYHU - 专注互联网开发及运营技术,提供相关资料及软件下载,奇趣网络时事评论!
Apr 2

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有两个核心部分:Route和RouteHandler。一个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的机制了解了吧:)
▲返回顶部

Add a comment

Nickname

emotemotemotemotemotemotemotemotemotemotemotemotemotemotemotemot