2010-11-17 76 views
4

我一直在开发纯粹的MVC CMS以获得乐趣,并且遇到了烦人的ASP.NET路由错误/功能。在ASP.NET路由中扩展RouteCollection

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

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

看着反射器我看不到一个简单的方法来扩展这个集合,因为_namedMap字典被标记为私有。我尝试在插入点切入集合并重新添加每个项目,但是因为没有方法从RouteCollection中反向查找路由名称,所以我无法用它们之前可能已有的名称重新添加它们。太令人沮丧了!

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

对此处最佳解决方案有何建议?

编辑:

好吧,也许我本来应该很多很多清晰的在这里。我不是在寻找对我的CMS设计的批评。我很欣赏这些评论,但这不是我所要求的。

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

干杯,

伊恩

+0

为什么你正确地将路由映射到托管页面?您可能能够简化您的URL结构,以便您可以不断重复使用独立于托管页面的相同路由。 – 2010-11-17 16:56:49

+0

@Berin:啊,是的。我没有想到这一点。每页一条路线确实是一个糟糕的设计。应该使用现有路线中的参数值来识别页面。 – 2010-11-17 17:00:54

+0

我不是很清楚,例如我有一个托管的博客详细信息页面,其中包含post/{name}/{id}的url,我不必管理url解析。我有一个处理简单的静态URL的托管路由块的结尾,所以它不是每页一个路由,它的每个动态页面+ 1的路由。我的问题依然存在。 – madcapnmckay 2010-11-17 17:32:59

回答

0

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

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

+0

正确,但我会因此而失去所有现有路线的名称信息。由于名称信息存储在private _namedMap字典中,因此它不是路由本身的一部分。需要的是一个Insert(名称,RouteBase路由)方法。 – madcapnmckay 2010-11-18 11:22:01

+0

不是'RouteData.DataTokens'包含您可以简单提取的信息吗?更多信息 - http://stackoverflow.com/questions/363211 – 2010-11-18 11:27:36

-2

简短的回答:你不插入路由动态。当需要改变它们时,他们需要从头开始重建。这有几个原因,最重要的是确保路由系统不是应用程序的瓶颈。基本上,这组路径是,意图是是映射大量URL的一组静态资源。关于路由系统的一切都是在设计时考虑到的。

这是许多开发者的出发点,特别是来自基于文件的框架(如无路由WebForms项目)。它的确会迫使你以不同的方式思考URL。 http://en.wikipedia.org/wiki/Representational_State_Transfer


URL路由最近被Ruby on Rails的,这反过来又得到了从具象状态传输(REST)一纸想法流行起来。这个概念在Rails之前就存在了,但它是一个延续到ASP.NET MVC的概念。

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

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

这意味着该URL的一部分匹配,其中“{}控制器”字将被保存在“控制器”参数。与{action}和{id}一样。这意味着,你可以有共同的逻辑管理的网页是这样的:

/Page/Details/I eat spinach 

这被映射如下:

  • 控制器=“页面”(映射到的PageController类在你的控制器目录)
  • 行动=“详细资料”(映射到详细方法上的PageController类)
  • 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 overidden。这意味着一个空白的URL总是与ManagedPageController.Details("Home")匹配。如果你想编辑页面,URL可能看起来像“我吃菠菜/编辑”。

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

+0

呃。感谢您的努力,但我很清楚路由如何工作。我的问题是如何动态地将路由插入到ASP.NET路由中的RouteCollection中。 – madcapnmckay 2010-11-17 18:35:34

+0

我的答案是,我相信你会让它变得更加复杂而不是必要的。 – 2010-11-17 21:09:16

0

看看IRouteConstraint接口。基本上你添加一个catch所有的路由在应用程序启动时添加集合的结尾,这以一个约束对象作为参数。在这里,你会在你的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参数值是否对约束​​有效。