这个问题似乎与注册区域路线的顺序有关。由于我没有找到任何关于如何映射区域路由的结论性文档,并且不认为依赖任何特定的排序是安全的,所以我继续创建了一个新类来封装我可以从不同的路由中调用的路由路线文件。这基本上“模块化”了一个区域,以便它可以用作一组路由,以附加到其他一些 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
}
}