Hello World
Spiga

浅谈Route规则名称的作用,及MvcPatch的处理方式

2009-10-13 10:00 by 老赵, 16394 visits

国庆前的最后一天,我写了《关于ASP.NET Routing的几点内容》,其中谈论了ASP.NET Routing作用,设计目的,工作流程等等。不过我还有一个比较重要的东西一笔带过了,不知道您注意到了没有,在向ASP.NET Routing的RouteTable.Routes属性(一个RouteCollection对象)中添加Route规则的时候,我们会同时指定一个“名称”,在微软给出的官方“广告”中,似乎看不出这个名称有什么用。但事实上,它的功能非常关键。

那么我们通过一个实际的例子来看一下,Route规则的“名称”到底有什么作用:

routes.MapRoute(
    "Article.List",                                         // route name
    "articles/{*searchCriteria}",                           // pattern
    new { controller = "Article", action = "List" });       // defaults

routes.MapRoute(
    "Product.List",                                         // route name
    "products/{*searchCriteria}",                           // pattern
    new { controller = "Product", action = "List" });       // defaults

以上两个Route规则,分别是“文章(article)”和“产品(product)”的搜索URL。与微软“广告”中一直使用的{controller}/{action}/{id}不同,我们实际生产过程中,一些如controller或action这样的数据无法从URL中捕获到,因此我们只是“匹配URL”并且“指定默认值”。就“捕获URL”这方面,这样的配置非常完美。但是我也多次强调,Route规则还会负责“生成虚拟路径”。那么这样的配置在这方面会有什么问题呢?请看下面这个数据:

var data = new { controller = "Product", action = "List", searchCriteria = "..." }

我们可以使用这个匿名对象来构造一个RouteValueDictionary,其中包含了生成虚拟路径所需要的数据。于是我们可能会调用这个方法来得到结果:

RouteTable.Routes.GetVirtualPath(requestContext, new RouteValueDictionary(data));

很显然,这个数据是要生成“产品”的搜索路径,但是您猜一下,我们能够得到正确结果吗?

不行。

因为RouteCollection的GetVirtualPath方法会依次遍历其中所有Route对象,访问其GetVirtualPath方法,并把第一个非null参数作为结果。如果项目中的配置如以上两个,则这样的数据永远会被Article.List这个规则捕获到——于是生成的永远便是Article.List的虚拟路径了。

对于这个问题一般有两种解决方法,一是为Route规则添加“约束”:

routes.MapRoute(
    "Article.List",                                         // route name
    "articles/{*searchCriteria}",                           // pattern
    new { controller = "Article", action = "List" },        // defaults
    new { controller = "Article", action = "List" });       // constraints

于是,这个Route对象在生成虚拟路径的时候,就会关注当前的RouteValueDictionary中包含的值是否符合约束。如果不符合,则直接返回null。如此,RouteCollection便会继续尝试下一个Route规则——可能就是Product.List,于是我们得到了想要的数据。

第二种解决方法便是在构造URL的时候指定一个名称:

RouteTable.Routes.GetVirtualPath(requestContext, "Product.List", new RouteValueDictionary(data));

如此,RouteCollection便会直接去找到Product.List所对应的Route对象,并且直接使用它的GetVirtualPath方法获得虚拟路径——即使返回null,也不会尝试其他Route规则。这么做的好处是避免Route规则之间在生成虚拟路径时产生冲突,并且由于避免了便利所造成的无效尝试,于是程序性能也会有些许提高。

MvcPatch中也有对Route配置名称的支持:在MvcPatch.Extensions.dll中包含一个RouteNameAttribute对象,我们可以用它对某个Action方法进行标记,如:

[RouteName("Product.List")]
public ActionResult List(string searchCriteria)
{
    ...
}

于是,如果您在View中使用这样的方法来构造URL:

<%= Url.Action<ProductController>(c => c.List(...)) %>

MvcPatch便会查看这个Action方法是否有RouteName标记。如果有,则通过直接指定名称的方式去构造虚拟路径,否则便还是传统的“遍历”方式。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

9 条回复

  1. lexc[未注册用户]
    *.*.*.*
    链接

    lexc[未注册用户] 2009-10-13 10:02:00

    您太高产了

  2. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2009-10-13 10:06:00

    路过看看,等玩mvc的时候再研究

  3. EricWen
    *.*.*.*
    链接

    EricWen 2009-10-13 10:09:00

    能讲的再详细点吗?

  4. 老赵
    admin
    链接

    老赵 2009-10-13 10:17:00

    @EricWen
    你是指哪个部分呢?

  5. 都行啊[未注册用户]
    *.*.*.*
    链接

    都行啊[未注册用户] 2009-10-13 10:44:00

    基于WebForm的ASP.NET和基于MVC的ASP.NET虽然本质不同,但是各有优缺点,选择适合自己的最重要,毕竟MVC做到的WebForm也可以做到

  6. 老赵
    admin
    链接

    老赵 2009-10-13 10:46:00

    @都行啊
    没错。

  7. 老赵
    admin
    链接

    老赵 2009-10-13 21:43:00

    好像大家对于ASP.NET MVC没有啥感觉啊,呵呵。

  8. lyamlf123
    *.*.*.*
    链接

    lyamlf123 2009-10-14 00:50:00

    原来route name真的可以起到生成虚拟路径的作用。

    我现在就是在MVC,感觉在设计route规则的时候,总会出现重复的情况,从而无法生成正确的URL(但有时错误的URL,既然能得到正确的结果)

    但如果通过route name 来,那就方便了。

  9. 链接

    yicone 2010-04-18 01:04:25

    写得好, stackoverflow.com也见过类似的讨论.

    读后感+个人经验, 觉得应该优先使用Route约束的方式解决这类问题.

    如果必须使用第二种---"构造URL的时候指定一个名称", 处理时也要考虑一些简单的技巧.
    具体到Html.BeginRouteForm(接受routeName参数的BeginFrom"加强版"), 考虑写一个HtmlHelper的扩展方法,
    例如, 实现BeginAbcRouteForm方法, 以对应 abcRoute (即routeName="abcRoute, 上文中"Product.List"这样的命名方式可能不大好),

    以这样的方式:
    防止routeName拼写错误;
    routeName变更带来的维护问题;
    提高重用(BeginRouteForm需要指定的参数从乘法角度远多于BeginForm的胃口)

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我