代码之家  ›  专栏  ›  技术社区  ›  madcapnmckay

在ASP.NET路由中扩展RouteCollection

  •  4
  • madcapnmckay  · 技术社区  · 15 年前

    我一直在开发一个纯粹的MVCMS来取乐,并且遇到了一个令人讨厌的错误/ASP.NET路由功能。

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

    问题是,新路由不需要简单地添加到RouteCollection的末尾,而是可能需要插入到特定位置。除了RouteCollection只包含标准 Insert(int idx, RouteBase route) 方法继承自 Collection<T> 不包含路由名称。路由的名称很重要,因为我在整个过程中使用它来生成动作链接。

    看着Reflector,我看不出一个简单的方法来扩展这个集合,因为namedmap字典被标记为private。我尝试在插入点截断集合,然后重新添加每个项,但是由于没有从RouteCollection反向查找路由名称的方法,我无法使用它们以前的名称重新添加它们。太令人沮丧了!!!!

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

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

    编辑:

    好吧,也许我应该 非常多 这里更清楚。我不想批评我的CMS设计。我很欣赏这些评论,但这不是我想要的。

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

    干杯,

    伊恩

    3 回复  |  直到 15 年前
        1
  •  0
  •   Dan Atkinson    15 年前

    如果你看一下 ClearItems() 方法,这应该提供一种清空路由的方法。如果首先将Routes集合移动到临时集合中(并在其中插入新的路由),请运行clearitems(),然后使用重新填充 Add() .

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

        2
  •  0
  •   Pauli Østerø    15 年前

    看看 IRouteConstraint 接口。基本上,您添加了一个catch all路由,在应用程序启动时添加集合的结尾,它以一个约束对象作为参数。在这里,如果传入的URL与有效页面匹配,您将在CMS中查找,然后返回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参数值是否对约束有效。

        3
  •  -2
  •   Berin Loritsch    15 年前

    简短回答:您不会动态插入路由。当需要改变它们时,它们需要从头开始重建。这有几个原因,最主要的原因是确保路由系统不是应用程序的瓶颈。基本上,一组路线是 打算 是映射大量URL的一小部分静态资源集。有关路由系统的所有内容都是在设计时考虑到的。

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


    URL路由最近受到RubyonRails的欢迎,而RubyonRails又从一篇关于表示状态转移(REST)的论文中得到了这个想法: http://en.wikipedia.org/wiki/Representational_State_Transfer . 这个概念早在Rails之前就存在了,但它是一个被转移到ASP.NET MVC的概念。

    RESTful路由背后的原理是它们有一个共同的结构,只有某些部分发生了变化。在应用程序中,它将是托管页的名称。默认情况下,ASP.NET与您的路由匹配如下:

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

    这意味着与“控制器”字匹配的URL部分将存储在“控制器”参数中。与动作和ID相同。这意味着您可以为这样的托管页面使用公共逻辑:

    /Page/Details/I eat spinach
    

    映射如下:

    • controller=“page”(映射到控制器目录中的pagecontroller类)
    • action=“details”(映射到pagecontroller类上的details方法)
    • id=“i eat spoice”(作为详细信息操作的参数传递)。

    您的控制器代码将具有如下方法:

    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") . 如果你想编辑页面,网址可能看起来像“我吃菠菜/编辑”。

    对于“id”参数有一些警告,这些警告与MVC试图从中保存的禁止字符有关。如果强制页面名称不要包含这些禁止字符,则问题会少得多。