我有一个区域Foo
,它有一个映射的路线:foo/{controller}/{action}/{id}
。
我还有另一个区域Bar
,它本质上是该区域的一个子组件。它的映射路由为foo/bar/{controller}/{action}/{id}
。
因此,例如,我在BazController
区域内有一个名为Bar
的控制器,因此我可以使用类似foo/bar/baz
的路径。
问题在于这种情况似乎没有解决路由,因为看起来我的映射路由正在寻找名为BarController
的控制器,而不是映射到用{{1}声明的路由}}
我假设有一些我无意中违反的基本设计概念......如果是这样的话,我应该如何组织这个而不是有两个区域?
我对url路由的主要理解来自Django背景,您可以在其中执行诸如引用单独的url文件之类的内容,并且所有路由都以自上而下的方式处理。我不知道如何使用asp.net mvc确定路由映射优先级,并且不知道如何使用区域进行路由注册的排序。
更新
我使用Phil Haack的路由调试器作为@zLan建议,它确实匹配我的两个映射路由,并且由于某种原因优先于foo/bar/{controller}/{action}/{id}
区域中指定的路由超过Foo
区域中指定的路由{1}}区域。
我进一步调试了它并在Global.asax中指定了两个路由,而不是像@mfanto建议的那样在各自的Bar
方法中指定,并且它似乎选择了首先声明的路由。
我的后续问题是:如何指定/确定首先注册哪个区域?如果这不是一个可靠的约定,是否有一种可接受的方式来声明这些路由,以便网址RegisterArea
将解析到我的foo/bar/baz
区域,而不必在Bar
区域全部声明它们{1}}?
答案 0 :(得分: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很适合修复这样的问题。
答案 1 :(得分: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
}
}