2012-03-30 114 views
0

我有一个区域,Foo,它具有映射的单个路由:foo/{controller}/{action}/{id}asp.net mvc区域作为另一个区域的一部分路由

我也有另一个区域,Bar,这实质上是这个区域的一个子组件。它具有映射路线foo/bar/{controller}/{action}/{id}。因此,例如,我在我的Bar区域内有一个名为BazController的控制器,因此我可以有一条类似foo/bar/baz的路线。

这样做的问题是,路线似乎并没有为解决这种情况,因为它似乎是我的映射路线正在寻找一个名为BarController,而不是映射与foo/bar/{controller}/{action}/{id}

我宣布路由控制器假设有一些基本的设计理念,我不知不觉地违反了......如果是这样的话,我应该如何组织这个,而不是有两个领域?

我对url路由的基本理解来自Django背景,您可以在其中执行诸如引用单独url文件之类的内容,并且所有路由都以自顶向下的方式进行处理。我不知道如何使用asp.net mvc确定路由映射优先级,也不知道如何对区域进行路由注册排序。

UPDATE

我用菲尔哈克的路线调试器@zLan建议,它确实是匹配在我的两个映射路线,出于某种原因,采取优先于Foo面积超过了一个指定的路线在Bar区域指定。

我进一步调试它,并且在Global.asax中而不是在其各自的RegisterArea方法中指定了两个路由,就像@mfanto建议的那样,它似乎选择了首先声明的路由。

我的后续问题是:我如何指定/确定哪个区域会首先被注册?如果这不是一个可靠的公约,是否有一种可接受的方式来声明这些路线,以便网址foo/bar/baz将解析为我的Bar区域,而无需在Global.asax中声明它们全部?

+0

菲尔哈克有很大的[路线调试器(http://haacked.com/archive/2008/03/ 13/url-routing-debugger.aspx),让你知道你的路线在哪里。 – 2012-03-30 16:08:27

+0

您可以粘贴到目前为止创建的路线列表吗? – mfanto 2012-03-30 16:11:32

回答

0

这个问题似乎与地区路线登记的顺序有关。由于我没有找到有关区域路线如何映射的确凿文档,也不认为依赖任何特定顺序是安全的,我继续创建一个新类来封装我可以从不同的路径调用的路由路线文件。这基本上“模块化”了一个区域,以便它可以用作一组路由来附加到其他一些URL前缀。

的基本思想是仿效Django's ability to include other urlconfs,这样我可以做类似下面的内FooAreaRegistration

public override void RegisterArea(AreaRegistrationContext context) 
{ 
    new BarAreaModule().RegisterRoutes(namePrefix:"bar", urlPrefix:"foo/", context.Routes); 

    context.MapRoute("foo_default", "foo/{controller}/{action}/{id}", new{controller="Default", action="Index", id=UrlParameter.Optional}); 
} 

,并拥有所有的路线获得注册以有序的方式,使foo/bar没有得到混淆``foo_default“路线。

以下是AreaModule类的全部源代码,任何人这是兴趣:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web.Mvc; 
using System.Web.Routing; 

namespace Gov.Wa.Hecb.UI.Portal.Areas 
{ 
    /// <summary> 
    /// Allows a group of functionality to be registered to any given url prefix. This is to be used to replace an Area in cases 
    /// where an Area is either a sub-module to another area, or if an area's functionality is to be parameterized and reused in 
    /// multiple urls. 
    /// </summary> 
    public abstract class AreaModule 
    { 
     public abstract string AreaName { get; } 

     /// <summary> 
     /// Registers all routes necessary for this Module to function with the given url prefix 
     /// </summary> 
     /// <param name="namePrefix"></param> 
     /// <param name="urlPrefix">a slash-appended string representing the url to match up to the module</param> 
     /// <param name="routes"></param> 
     public void RegisterRoutes(string namePrefix, string urlPrefix, RouteCollection routes) 
     { 
      if (string.IsNullOrEmpty(namePrefix)) 
       throw new ArgumentException("namePrefix cannot be null or empty", "namePrefix"); 
      if (string.IsNullOrEmpty(urlPrefix)) 
       throw new ArgumentException("urlPrefix cannot be null or empty", "urlPrefix"); 
      if (routes == null) 
       throw new ArgumentNullException("routes"); 

      var context = new AreaModuleContext(AreaName, namePrefix, urlPrefix, routes); 
      var thisNamespace = GetType().Namespace; 
      if (thisNamespace != null) 
       context.Namespaces.Add(thisNamespace + ".*"); 

      RegisterRoutes(context); 
     } 

     protected abstract void RegisterRoutes(AreaModuleContext context); 
    } 

    public class AreaModuleContext 
    { 
     #region Private 

     private readonly HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase); 

     #endregion 

     #region Constructors 

     public AreaModuleContext(string areaName, string namePrefix, string urlPrefix, RouteCollection routes, object state = null) 
     { 
      if (String.IsNullOrEmpty(areaName)) 
       throw new ArgumentException("areaName cannot be null or empty", "areaName"); 
      if (string.IsNullOrEmpty(namePrefix)) 
       throw new ArgumentException("namePrefix cannot be null or empty", "namePrefix"); 
      if (string.IsNullOrEmpty(urlPrefix)) 
       throw new ArgumentException("urlPrefix cannot be null or empty", "urlPrefix"); 
      if (routes == null) 
       throw new ArgumentNullException("routes"); 

      AreaName = areaName; 
      NamePrefix = namePrefix; 
      UrlPrefix = urlPrefix; 
      Routes = routes; 
      State = state; 
     } 


     #endregion 

     #region Properties 

     public string AreaName { get; private set; } 

     public string NamePrefix { get; private set; } 

     public string UrlPrefix { get; private set; } 

     public ICollection<string> Namespaces 
     { 
      get { return _namespaces; } 
     } 

     public RouteCollection Routes { get; private set; } 

     public object State { get; private set; } 

     #endregion 

     #region Route Mapping 

     public Route MapRoute(string name, string url) 
     { 
      return MapRoute(name, url, (object) null /* defaults */); 
     } 

     public Route MapRoute(string name, string url, object defaults) 
     { 
      return MapRoute(name, url, defaults, (object) null /* constraints */); 
     } 

     public Route MapRoute(string name, string url, object defaults, object constraints) 
     { 
      return MapRoute(name, url, defaults, constraints, null /* namespaces */); 
     } 

     public Route MapRoute(string name, string url, string[] namespaces) 
     { 
      return MapRoute(name, url, null /* defaults */, namespaces); 
     } 

     public Route MapRoute(string name, string url, object defaults, string[] namespaces) 
     { 
      return MapRoute(name, url, defaults, null /* constraints */, namespaces); 
     } 

     public Route MapRoute(string name, string url, object defaults, object constraints, string[] namespaces) 
     { 
      if (namespaces == null && Namespaces != null) 
       namespaces = Namespaces.ToArray(); 

      var route = Routes.MapRoute(NamePrefix + name, UrlPrefix + url, defaults, constraints, namespaces); 
      route.DataTokens["area"] = AreaName; 

      // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up 
      // controllers belonging to other areas 
      var useNamespaceFallback = (namespaces == null || namespaces.Length == 0); 
      route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback; 

      return route; 
     } 

     #endregion 
    } 
} 
0

在Global.asax中为RegisterRoutes()添加类似如下的内容为路由工作吗?

routes.MapRoute(
       "Default", // Route name 
       "foo/bar/{controller}/{action}/{id}", // URL with parameters 
       new { area = "Bar", controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults 
      ); 

为了回应zLan的评论,Phil的Route Debugger非常适合解决这样的问题。

+0

通过在'Global.asax'中声明两条路由,它似乎接受声明的第一条路由。这样做似乎破坏了将所有路线注册保留在指定区域内的目的。也许我在“嵌套”区域做的事情需要我这样做呢? – 2012-03-30 17:19:27