如何使用 ASP.NET 路由来路由树形结构的 URL?
我想实现与这个问题非常相似的东西,有一些增强功能。
有一个 ASP.NET MVC Web 应用程序。
我有一棵实体树。
例如,Page
类具有名为 Children 的属性,其类型为 IList
。 (Page
类的实例对应于数据库中的一行。)
请注意,网站所有者可以随时添加新页面或删除现有页面,并且 URL 应将这些更改反映为出色地。
我想为数据库中的每个 Page
分配一个唯一的 URL。
我使用名为 PageController
的控制器处理 Page
对象。
示例 URL:
http://mysite.com/Page1/
http://mysite.com/Page1/SubPage/
http://mysite.com/Page/ChildPage/GrandChildPage/
您将获得图片。
因此,我希望每个 Page
对象都有自己的 URL,该 URL 等于其父级 URL 加上自己的名称。
除此之外,我还希望能够将单个 Page
映射到 /
(根)URL。
我想应用这些规则:
- 如果可以使用任何其他路由处理 URL,或者指定 URL 中的文件系统中存在文件,则进行默认 URL 映射
- 如果可以处理 URL由虚拟路径提供程序,让它处理它
- 如果没有其他,将其他 URL 映射到
PageController
类
我还发现这个问题,以及还有这个和这个,但是他们没有多大帮助,因为他们没有对我的前两点提供解释。
我看到以下可能的解决方案:
- 分别为每个页面映射一条路线。
这要求我在应用程序启动时遍历整个树,并将完全匹配的路由添加到路由表的末尾。 - 我可以使用
{*path}
添加一条路由,并编写一个自定义的IRouteHandler
来处理它,但我不知道如何处理前两个规则,因为这个处理程序可以处理所有事情。
到目前为止,第一个解决方案似乎是正确的,因为它也是最简单的。但即使在这种情况下,我仍然不确定如何使 PageController
来处理请求。
我非常感谢您对此的想法。
先感谢您!
编辑: 我现在有时间检查我收到的每个答案的各个方面。我接受了尼尔的回答,因为他是对事情如何运作提供最好解释的人。我还对所有其他答案都投了赞成票,因为它们提供了很好的想法。
I would like to achieve something very similar to this question, with some enhancements.
There is an ASP.NET MVC web application.
I have a tree of entities.
For example, a Page
class which has a property called Children, which is of type IList<Page>
. (An instance of the Page
class corresponds to a row in a database.)
Note that the owners of the site can add a new page anytime, or delete existing ones, and the URLs should reflect those changes as well.
I would like to assign a unique URL to every Page
in the database.
I handle Page
objects with a Controller called PageController
.
Example URLs:
http://mysite.com/Page1/
http://mysite.com/Page1/SubPage/
http://mysite.com/Page/ChildPage/GrandChildPage/
You get the picture.
So, I'd like every single Page
object to have its own URL that is equal to its parent's URL plus its own name.
In addition to that, I also would like the ability to map a single Page
to the /
(root) URL.
I would like to apply these rules:
- If a URL can be handled with any other route, or a file exists in the filesystem in the specified URL, let the default URL mapping happen
- If a URL can be handled by the virtual path provider, let that handle it
- If there is no other, map the other URLs to the
PageController
class
I also found this question, and also this one and this one, but they weren't of much help, since they don't provide an explanation about my first two points.
I see the following possible soutions:
- Map a route for each page invidually.
This requires me to go over the entire tree when the application starts, and adding an exact match route to the end of the route table. - I could add a route with
{*path}
and write a customIRouteHandler
that handles it, but I can't see how could I deal with the first two rules then, since this handler would get to handle everything.
So far, the first solution seems to be the right one, because it is also the simplest. But still, even in that case I'm not sure how could I make the PageController
to handle the requests.
I would really appreciate your thoughts on this.
Thank you in advance!
EDIT:
I now had the time to examine every aspects of every answer I received. I accepted Neal's answer, since he is the one providing the best explanation about how things work. I also upvoted all other answers, since they provide good ideas.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
路由按照添加到集合中的顺序进行处理。您可以在现有路由之后添加自定义路由,以确保它是最后一个有机会处理请求的路由。这将允许您在其之前添加现有文件(虚拟或其他)的路由,从而满足条件 1 和 2。
默认情况下,MVC 路由将在应用路由集合中存储的任何路由之前路由到现有文件;请参阅 http://msdn.microsoft.com /en-us/library/system.web.routing.routecollection.routeexistingfiles.aspx。 (给保罗的帽子 - 见评论)。
要将请求路由到页面控制器,只需创建一个自定义路由来检查虚拟路径,如果它与数据库中页面的模式匹配,则返回
RouteData
。使用从虚拟路径中提取的适当值(例如,将 Path 键设置为 /Parent/Child/Grandchild)设置您的RouteData
,将控制器键设置为您的页面控制器名称(例如 Page)和action 为您要执行的操作的名称(例如显示)。RouteData
应使用MvcRouteHandler
创建(不确定这是否是正确的类名)。为了确保正确返回数据库驱动页面的 URL,请重写
RouteBase
的GetVirtualPath( RequestContext, RouteValueDictionary )
方法,并使用传入的路由值来确定这是否是数据库驱动页面,如果它创建所需的虚拟路径数据(否则返回 null)。如需重写
GetRouteData
和GetVirtualPath
的帮助,请查看System.Web.Routing.RouteBase
和System.Web 的反映源代码.Routing.Route
;之后谷歌就是你的朋友。路由反向用于确定给定控制器、操作和任何其他路由值的 url。您应该能够利用它在请求的上下文中构建页面的 URL。
Routes are processed in the order they are added to the collection. You could add your custom route after the existing routes to ensure it is the last one to get a chance at handling the request. This will allow you to add routes for existing files (virtual or otherwise) before it and therefore meet criteria 1 and 2.
By default, MVC routing will route to existing files before applying any routes stored in the route collection; see http://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.routeexistingfiles.aspx. (hattip to Paul - see comments).
To route requests to your page controller, simply create a custom route that examines the virtual path and if it matches the pattern for a page in the database returns the
RouteData
. Set up yourRouteData
with the appropriate values extracted from the virtual path (e.g. set the Path key to /Parent/Child/Grandchild), set the controller key to your page controller name (e.g. Page) and the action to the name of the action that you want executed (e.g. Show). TheRouteData
should be created with theMvcRouteHandler
(not sure if that is the correct class name).To ensure that urls to your database driven pages are returned correctly, override the
GetVirtualPath( RequestContext, RouteValueDictionary )
method ofRouteBase
and use the route values passed in to determine if this is a database driven page and if it is create the virtual path data required (or return null otherwise).For help with overriding
GetRouteData
andGetVirtualPath
, look at the reflected source code ofSystem.Web.Routing.RouteBase
andSystem.Web.Routing.Route
; after that Google is your friend.Routes are used in reverse to determine the url given the controller, action and any other route values. You should be able to utilise this to build the url of the page within the context that it is being requested.
一种不同的想法是使用 T4(文本模板转换工具包)读取一次 Children 并生成 Global.asax 文件的内容。
编辑:基本上使用 T4,您可以自动生成文本文件。例如,您可以不手动复制某个庞大集合的项目并将其与某些特定上下文一起粘贴到文本文件中(例如
INSERT INTO [MyTable] (Text) VALUES (@ItemText)
) T4 引擎读取集合并为您生成这些插入语句。它是静态的,不适用于运行时。我发现 Pro Entity Framework 4.0 书中提供了非常好的介绍。
但如果您说需要动态执行此操作,那么这可能不适合您。
One different idea is to use T4 (Text Template Transformation Toolkit) to read your Children once and generate the content of your Global.asax file.
EDIT: Bascially with T4 you can automate text file generation. For instance, instead of manually copying out items of some huge collection and pasting them with some specific context into a text file (like
INSERT INTO [MyTable] (Text) VALUES (@ItemText)
) you could have a T4 engine read the collection and generate these insert statements for you. It is static and not meant for a runtime.I find a very good introduction is available from the Pro Entity Framework 4.0 book.
But if you say you need to do it dynamically, this may not be the tool for you.
保存页面时您就知道页面结构。因此,您可以为每个页面生成 URL 并将其保存到数据库记录中。然后您可以使用
{*path}
规则并在数据库中查找完全匹配。该规则应位于规则定义的最后,以便您可以匹配其他路由。例如,您的
Page1
没有父页面,它的 url 是Page1
。您的SubPage
知道它的父级,因此它可以获取 urlPage1/SubPage
等。You know your pages structure when you save page. So, you can generate URL for each page and save it in to the database record. Then you can use
{*path}
rule and find exact match in database. This rule should be last in your rules definition, so you can match other routes.For example, your
Page1
has no parent page, it's url isPage1
. YourSubPage
knows it's parent so it can ganarate urlPage1/SubPage
etc.您可以使用
"Page/{*path}"
模式。然后,您可以通过在“/”上拆分字符串并遍历它来分解路径,或者您可以使用 Rarouš 的建议,将[生成的]路径存储在数据库中并进行直接查找。如果您使用 Rarouš 的方法,那么当父路径更改时,您将必须更新表中所有子项的路径条目。这可以通过单个更新查询简单地完成。
我假设您正在将要用于主页的页面映射到配置文件或表条目中的某个位置。您可以让主页控制器执行查找并返回主页视图要呈现的内容(您可以使用共享视图、部分视图或调用页面控制器,这样就不会重复行为),或者您可以将其重定向到该页面。
使用这种技术,您可以拥有一个页面控制器和视图,以相同的方式处理所有这些页面。您的其他需求似乎是由 MVC 框架自动处理的。
您的路径将如下所示:
您当然可以使用“Page”以外的前缀。
You could use a
"Page/{*path}"
pattern. You can then either decompose path by splitting the string on ‘/’ and walk that, or you can use Rarouš’ suggestion of storing the [generated] path in the DB and do a direct lookup.If you use Rarouš’ method then you will have to update path entries in your table for all the children when the parent path changes. This can be done simply enough with a single update query.
I’m assuming that you are mapping the page you wish to use for the home page somewhere in a config file, or table entry. You can have your home page controller either do the lookup and return the content for the home page view to render (you can use a shared view, partial view, or call into the page controller so that you don’t duplicate behaviour), or you can have it redirect to that page.
Using this technique you can have a single page controller and view that handles all these pages in the same way. Your other requirements seem to be handled automatically by the MVC framework.
Your path would look like this:
You can of course use a prefix other than "Page".