技术开发 频道

深入理解ASP.NET MVC路由表生成URL原理

    【IT168 技术文档】URL是如何通过路由表生成的(outbound)

  通常我们被推荐在view设计时使用Html.ActionLink(…)产生链接,这样做的优势就是,url可以根据路由表生成。路由机制的另一个职责便是根据路由表生成url,而不是由我们手动编写。接下来深入分析一下其原理。

  类似Html.ActionLink的方法,最终都将通过查询路由表来生成url,跟inbound一样,路由表总是按顺序被依次遍历,直到匹配。每个Route的GetVirtualPath方法负责审查,该方法接受一个RouteValueDictionary参数,该参数实际上就是由相应的Helper提供的。当以下三个条件成立时,认为匹配成功:

  一、每个由大括号定义的参数必须能够找到对应的值。按照优先顺序这些值将有下面三种来源:

  1.RouteValueDictionary参数明确提供的。也就是我们在调用某些有生成url功能的Helper时提供的,比如ActionLink方法;

  2.*从当前请求的上下文的RouteData中;关于这点有更特殊的情况,下面将详细讨论;

  3.Route的Defaults中。

  二、如果每个Defaults集合中的参数与RouteValueDictionary参数中的值都符合,而且这些值没有在url pattern中用大括号定义,那么认为匹配。听起来有点拗口,下面举个例子.

  假如有如下规则:

routes.MapRoute( null,
    
"manage/orders/{page}",
    
new { controller = "Admin",
    action
= "OrderManage", page = UrlParameter.Optional });

 

  页面有如下链接:

<a href="<%:Url.Action("OrderManage", "Admin") %>">Order</a>

 

  Defaults中有两个参数controller和action,在url的pattern中没有类似{controller}和{action},而且Url.Action的参数同时提供了与Defaults设置一致的参数值(这已经够了),于是这个url是匹配的。

  三、每个参数都符合Constraints的匹配规则。

  满足上面三个条件后,GetVirtualPath就会将相应的参数填入url中,并最终返回url的字符串。需要强调的一点是:MVC框架的这种行为工作在懒惰模式下,一旦“该要的要到了”,那么就停止继续遍历。这也从另一个角度说明了,把特殊的往前放的黄金原则。

  举个例子:

  下面是两个有顺序的Route:

routes.MapRoute(
                
null,
                
"manage/car",
                
new { controller = "Admin", action = "Car" }
                );
routes.MapRoute(
                
null,
                
"manage/car/{operation}",
                
new { controller = "Admin", action = "Car" }
                );

 

  有一个链接表示:

<%: Html.ActionLink("Add Car", "Car","Admin", new { operation = "add" }, null )%>

 

  最后的结果是什么呢?是manage/car/add吗?

  其实最后的结果是manage/car,为什么呢?对于第一个Route,它期望的是controller和action这两个参数,而上面的ActionLink的确完整的提供了这两个参数,而且与Defaults设置一致(这满足规则的第二条),于是匹配了!即使还提供了operation参数,框架也不管了。显然这不是希望的。只要把两个Route的位置换一下即可。这个例子只是一个演示。

  *关于上面条件一的第二小点,其实是个很棒的特性,这里补充一个例子:

  假设当前页面的url是:

/Catalog/List/Purple/123

routes.MapRoute(
null, "{controller}/{action}/{color}/{page}");
<%: Html.ActionLink("Click me", "List", "Catalog", new {page=789}, null) %>

 

  这个link能匹配这个route吗?也许你认为我没有提供color参数啊,怎么能匹配呢?事实上,结果确出人意料的是:

<a href="/Catalog/List/Purple/789">Click me</a>

 

  因为当前页面是/Catalog/List/Purple/123,此时的RouteData保存了color参数值,尽管在ActionLink中没有显示设置color,但是仍然从当前的RouteData中找到了color参数。

  再看一个例子: 

  当前页面的url还是:

/Catalog/List/Purple/123

routes.MapRoute(
null, "{controller}/{action}/{color}/{page}");
<%: Html.ActionLink("Click me", "List", "Catalog", new {color="Aqua"}, null) %>

  这个link还能匹配这个route吗?也许你认为根据上面的规则应该能,但结果却是无法匹配,为什么呢?因为有个原则,RouteData中的数据只能用于填充url pattern中“靠前的参数”,最后一个参数是不能的!

  最后关于outbound还有一个原则,Defaults提供的值,会尽可能的不出现在最终的url结果中。 

0
相关文章