【问题标题】:Extending the RouteCollection in ASP.NET Routing在 ASP.NET 路由中扩展 RouteCollection
【发布时间】:2011-05-11 09:54:44
【问题描述】:

我一直在开发纯粹的 MVC CMS,但遇到了一个烦人的 ASP.NET 路由错误/功能。

我的 CMS 中的每个 动态 托管页面都与从数据库中提取的特定路由相关联。这些在应用程序启动时加载。当用户添加新页面或编辑现有页面的 Url 时,我需要能够编辑 RouteTable 以相应地插入/编辑路由。

问题是新路由不需要简单地添加到 RouteCollection 的末尾,而是可能需要插入到特定位置。除了 RouteCollection 仅包含从 Collection<T> 继承的标准 Insert(int idx, RouteBase route) 方法之外,这似乎足够合乎逻辑,该方法不包含路由名称。路线的名称很重要,因为我始终使用它来生成操作链接。

查看反射器,我看不到扩展此集合的简单方法,因为 _namedMap 字典被标记为私有。我尝试在插入点切断集合并再次重新添加每个项目,但是因为没有方法可以从 RouteCollection 反向查找路线的名称,所以我无法使用它们之前可能拥有的名称重新添加它们。好郁闷!!!

为什么路由的名字不是路由对象的属性? 如果 MS 认真对待我们扩展 MVC 和路由,为什么它们会使关键类难以扩展?

关于这里的最佳解决方案有什么建议吗?

编辑:

好吧,也许我应该在这里更清楚。我不是在寻找对我的 CMS 设计的批评。我很欣赏cmets,但这不是我要问的。

简化问题。如何在运行时将命名路由插入到路由集合中?类的当前插入方法不充分,因为它不包含名称。

干杯,

伊恩

【问题讨论】:

  • 为什么您将路由一对一映射到托管页面?您或许可以简化您的 URL 结构,这样您就可以独立于托管页面重复使用相同的路由。
  • @Berin:啊,是的。我没有想到这一点。每页一条路线确实是一个糟糕的设计。应该使用现有路由中的参数值来识别页面。
  • 我不太清楚,例如我有一个托管博客详细信息页面,其 url 为 post/{name}/{id},我不必管理 url 解析。我在处理简单静态 url 的托管路由块的末尾有一个全部内容,因此它不是每页一个路由,而是每个动态页面一个路由 + 1。我的问题仍然存在。
  • 我确实说过“托管”页面,所以我明白你在说什么。也许如果您对您希望 URL 的外观有所了解,我可以帮助您想出一个更简单的路由策略。
  • 嗯...缺少创建静态文件(并确保RouteExistingFilestrue),没有一种很好的方法来做你所要求的,而不是每次都强制重新启动应用程序CRUD 发生在路由上。

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


【解决方案1】:

简短回答:您不会动态插入路线。当需要改变它们时,它们需要从头开始重建。这有几个原因,大多数与确保路由系统不是您的应用程序的瓶颈有关。本质上,路由集旨在成为映射大量 URL 的小型静态资源集。路由系统的所有设计都考虑到了这一点。

这对许多开发人员来说是一种背离,尤其是来自基于文件的框架(如无路由的 WebForms 项目)。它确实迫使您以不同的方式考虑 URL。


最近,Ruby on Rails 使 URL 路由变得流行起来,它又从一篇关于 Representational State Transfer (REST) 的论文中得到了这个想法:http://en.wikipedia.org/wiki/Representational_State_Transfer。这个概念在 Rails 之前就已经存在,但它是一个被 ASP.NET MVC 继承的概念。

RESTful 路由背后的原则是它们具有共同的结构,只有某些部分发生变化。在您的应用程序中,它将是托管页面的名称。默认情况下,ASP.NET 会像这样匹配您的路由:

/{controller}/{action}/{id}

这意味着与“{controller}”单词所在位置匹配的 URL 部分将存储在“controller”参数中。 {action} 和 {id} 也一样。这意味着您可以拥有这样的托管页面的通用逻辑:

/Page/Details/I eat spinach

映射如下:

  • controller = "Page"(映射到 Controllers 目录中的 PageController 类)
  • action = "Details"(映射到 PageController 类的 Details 方法)
  • id = "我吃菠菜"(作为详细信息操作的参数传递。

你的控制器代码会有这样的方法:

public ActionResult Details(string id)
{
    return View(db.FindPage(id));
}

有了这些基础知识,我们就不再局限于这种结构。只要我们有一种方法可以提供到正确控制器的映射、正确的操作,并且可以通过 id 查找托管页面,我们就可以使 URL 成为我们想要的。假设我们希望页面名称排在第一位,动作排在第二位,我们根本不想担心控制器。我们将创建一个如下所示的路线:

routes.MapRoute(
    "ManagedPages",  // Route Name
    "{id}/{action}", // URL structure
    // Default route parameters
    new { controller = "ManagedPage", action = "Details", id = "Home" }
);

如果参数没有在 URL 中被覆盖,则提供默认值。这意味着空白 URL 将始终与 ManagedPageController.Details("Home") 匹配。如果您想编辑页面,URL 可能看起来像“我吃菠菜/编辑”。

“id”参数有一些注意事项,它们与 MVC 试图拯救您的禁止字符有关。如果您强制页面名称从不包含这些禁用字符,那么问题会少很多。

【讨论】:

  • 呃。感谢您的努力,但我很清楚路由是如何工作的。我的问题只是如何在 ASP.NET 路由中动态地将路由插入到 RouteCollection 中。
  • 我的回答是,我相信你让它变得比必要的复杂。
【解决方案2】:

如果您查看ClearItems() 方法,这应该提供了一种清空路线的方法。如果您首先将您的路线集合移动到一个临时集合中(并将您的新路线也插入其中),请运行 ClearItems(),然后使用 Add() 重新填充。

应该提到,您还应该使用GetReadLock()GetWriteLock() 以避免在您的应用程序中出现潜在的冲突。

【讨论】:

  • 正确,但这样做会丢失所有现有路线的名称信息。因为名称信息存储在私有 _namedMap 字典中,所以它不是路由本身的一部分。需要的是一个 Insert(name, RouteBase route) 方法。
  • RouteData.DataTokens 不包含您可以简单提取的信息吗?更多信息 - stackoverflow.com/questions/363211
【解决方案3】:

查看IRouteConstraint 接口。基本上,您在应用程序启动时添加一个捕获所有路由添加集合的末尾,它以一个约束对象作为参数。在这里,您将在您的 CMS 中查找传入 url 是否与有效页面匹配,然后返回 true 或 false,这将指示路由框架是否应考虑传入请求的路由

public interface IRouteConstraint
{
  bool Match(HttpContextBase httpContext, 
    Route route, 
    string parameterName, 
    RouteValueDictionary values, 
    RouteDirection routeDirection);
}

http://msdn.microsoft.com/en-us/library/system.web.routing.irouteconstraint.aspx

定义一个类必须实现的契约,以检查 URL 参数值是否对约束​​有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2014-02-09
    • 1970-01-01
    • 1970-01-01
    • 2013-04-26
    • 1970-01-01
    • 2016-02-14
    相关资源
    最近更新 更多