【问题标题】:How to route tree-structured URLs with ASP.NET Routing?如何使用 ASP.NET 路由路由树形结构的 URL?
【发布时间】:2011-02-22 19:45:38
【问题描述】:

我想实现与this question 非常相似的东西,并进行一些改进。

有一个 ASP.NET MVC Web 应用程序。

我有一棵实体树。
例如,Page 类具有名为 Children 的属性,其类型为 IList<Page>。 (Page 类的一个实例对应于数据库中的一行。)

请注意,网站所有者可以随时添加新页面或删除现有页面,并且 URL 也应反映这些更改。

我想为数据库中的每个 Page 分配一个唯一的 URL。
我使用名为PageController 的控制器处理Page 对象。

示例网址:

http://mysite.com/Page1/
http://mysite.com/Page1/SubPage/
http://mysite.com/Page/ChildPage/GrandChildPage/

你明白了。
因此,我希望每个 Page 对象都有自己的 URL,该 URL 等于其父 URL 加上自己的名称。
除此之外,我还希望能够将单个 Page 映射到 /(根)URL。

我想应用这些规则:

  1. 如果可以使用任何其他路由处理 URL,或者文件系统中存在指定 URL 的文件,则让默认 URL 映射发生
  2. 如果虚拟路径提供程序可以处理 URL,则让其处理
  3. 如果没有其他,则将其他 URL 映射到 PageController

我还找到了this questionalso this onethis one,但它们并没有太大帮助,因为它们没有对我的前两点提供解释。

我看到以下可能的解决方案:

  • 为每个页面单独映射路线。
    这需要我在应用程序启动时遍历整个树,并将完全匹配的路由添加到路由表的末尾。
  • 我可以使用{*path} 添加一个路由并编写一个自定义的IRouteHandler 来处理它,但是我看不出我该如何处理前两个规则,因为这个处理程序将处理所有事情。

到目前为止,第一个解决方案似乎是正确的,因为它也是最简单的。但是,即使在那种情况下,我也不确定如何让 PageController 来处理请求。

非常感谢您对此的看法。

提前谢谢你!

编辑: 我现在有时间检查我收到的每个答案的各个方面。我接受了尼尔的回答,因为他是对事情如何运作提供最佳解释的人。我还赞成所有其他答案,因为它们提供了很好的想法。

【问题讨论】:

    标签: .net asp.net asp.net-mvc routing asp.net-routing


    【解决方案1】:

    路由按照添加到集合中的顺序进行处理。您可以在现有路由之后添加自定义路由,以确保它是最后一个有机会处理请求的路由。这将允许您在其之前添加现有文件(虚拟或其他)的路由,因此符合标准 1 和 2。

    默认情况下,MVC 路由将在应用存储在路由集合中的任何路由之前路由到现有文件;见http://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.routeexistingfiles.aspx。 (给 Paul 的帽子 - 见 cmets)。

    要将请求路由到您的页面控制器,只需创建一个自定义路由来检查虚拟路径,如果它与数据库中页面的模式匹配,则返回RouteData。使用从虚拟路径中提取的适当值设置您的RouteData(例如,将 Path 键设置为 /Parent/Child/Grandchild),将控制器键设置为您的页面控制器名称(例如 Page),并将操作设置为您要执行的操作(例如显示)。 RouteData 应该使用 MvcRouteHandler 创建(不确定这是否是正确的类名)。

    为确保正确返回数据库驱动页面的 url,请覆盖 RouteBaseGetVirtualPath( RequestContext, RouteValueDictionary ) 方法并使用传入的路由值来确定这是否是数据库驱动页面以及是否创建虚拟路径需要数据(否则返回 null)。

    有关覆盖GetRouteDataGetVirtualPath 的帮助,请查看System.Web.Routing.RouteBaseSystem.Web.Routing.Route 的反射源代码;之后,Google 就是你的朋友了。

    在给定控制器、操作和任何其他路由值的情况下,反向使用路由来确定 url。您应该能够利用它在所请求的上下文中构建页面的 url。

    【讨论】:

    • 为什么以及如何为现有文件添加路由?另外,我将如何为树构建路由并将它们定向到适当的控制器?
    • 你不需要;默认情况下,路由系统将检查现有文件并倾向于提供该文件而不是尝试找到匹配的控制器和操作。
    • @Paul - 谢谢。这是否也适用于虚拟路径提供程序提供的文件?
    • @Venemo> 应该是两者; RouteCollection 有一个名为 _vpp 的字段,它是一个 VirtualPathProvider,它通过其构造函数注入(或将静态实例从 HostingEnvironment 中取出)。然后它调用 this._vpp.FileExists 并在文件存在时返回 null。返回 null 告诉系统路由子系统没有处理该请求。因此,除非由于某种原因它没有获得您正在使用的 VirtualPathProvider 的副本,否则它应该忽略这些文件。
    • @Paul - 非常感谢!
    【解决方案2】:

    一个不同的想法是使用 T4(文本模板转换工具包)读取您的孩子一次并生成 Global.asax 文件的内容。

    编辑:基本上使用 T4,您可以自动生成文本文件。例如,您可以让 T4 引擎读取集合并为您生成这些插入语句,而不是手动复制一些大型集合的项目并将它们与某些特定上下文一起粘贴到文本文件中(如 INSERT INTO [MyTable] (Text) VALUES (@ItemText))。它是静态的,不适用于运行时。

    我发现Pro Entity Framework 4.0 书中有一个很好的介绍。

    但如果您说需要动态执行,这可能不适合您。

    【讨论】:

    • 如果你能提供更多细节我会很高兴,我从未听说过 T4。顺便说一句,页面可以在运行时更改。网站管理员可以根据需要添加/删除/更改它们。
    • 感谢您指出它可能不适合我。实际上,集合在运行时会发生变化。不管怎样,谢谢你花时间回答我。 :)
    【解决方案3】:

    您在保存页面时就知道您的页面结构。因此,您可以为每个页面生成 URL 并将其保存到数据库记录中。然后您可以使用{*path} 规则并在数据库中查找完全匹配。此规则应位于您的规则定义中的最后一个,以便您可以匹配其他路由。

    例如,您的Page1 没有父页面,其网址为Page1。您的 SubPage 知道它是父级,因此它可以生成 url Page1/SubPage 等。

    【讨论】:

    • rarouš - 感谢您的回答!这是一个很好的 ida,但不会 {*path} 也捕获真实(和虚拟)文件名吗?
    【解决方案4】:

    您可以使用"Page/{*path}" 模式。然后,您可以通过拆分“/”上的字符串来分解路径并遍历它,或者您可以使用 Rarouš 将 [生成的] 路径存储在数据库中并进行直接查找的建议。

    如果您使用 Rarouš 的方法,那么当父路径更改时,您将必须更新表中所有子节点的路径条目。这可以通过一个更新查询来完成。

    我假设您正在将希望用于主页的页面映射到配置文件或表条目中的某个位置。您可以让您的主页控制器进行查找并返回要呈现的主页视图的内容(您可以使用共享视图、部分视图或调用页面控制器,这样您就不会重复行为),或者你可以让它重定向到那个页面。

    使用这种技术,您可以拥有一个以相同方式处理所有这些页面的页面控制器和视图。您的其他需求似乎由 MVC 框架自动处理。

    您的路径将如下所示:

    http://mysite.com/Page/Page1/ 
    http://mysite.com/Page/Page1/SubPage/ 
    http://mysite.com/Page/Page/ChildPage/GrandChildPage/ 
    

    您当然可以使用“Page”以外的前缀。

    【讨论】:

    • @Andre - 谢谢你的回答!这是迄今为止最好的主意。 :)
    • 您可以按从最具体到最不具体的顺序排列您的路线条目并删除前缀,使用 RouteTester (haacked.com/archive/2008/03/13/url-routing-debugger.aspx) 来检查您的路线。
    • @Venemo - 很高兴,你找到答案了吗?
    • @Andre - 每个答案都很好,但我还没有时间实施解决方案。 (这个项目是我在空闲时间做的事情,在过去的几周里我没有太多。)
    猜你喜欢
    • 1970-01-01
    • 2010-12-07
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 2016-03-08
    • 2017-03-12
    • 2016-09-17
    • 2010-09-21
    相关资源
    最近更新 更多