【问题标题】:asp.net mvc area routed as part of another areaasp.net mvc 区域作为另一个区域的一部分路由
【发布时间】:2012-03-30 16:03:53
【问题描述】:

我有一个区域 Foo,它映射了一条路线:foo/{controller}/{action}/{id}

我还有另一个区域Bar,它本质上是这个区域的一个子组件。它的映射路线为foo/bar/{controller}/{action}/{id}

因此,例如,我在 Bar 区域中有一个名为 BazController 的控制器,所以我可以有一个看起来像 foo/bar/baz 的路由。

问题在于路由似乎无法解决这种情况,因为我的映射路由似乎正在寻找一个名为 BarController 的控制器,而不是映射到使用 foo/bar/{controller}/{action}/{id} 声明的路由

我假设我无意中违反了一些基本的设计概念......如果是这样,我应该如何组织它而不是两个区域?

我对 url 路由的主要理解来自 Django 背景,在那里你可以做一些事情,比如引用单独的 url 文件,并且所有路由都以自上而下的方式处理。我不知道asp.net mvc是如何确定路由映射优先级的,也不知道路由注册的排序是如何通过area完成的。

更新

我按照@zLan 的建议使用了 Phil Haack 的路由调试器,它确实在我的两条映射路由上都匹配,并且由于某种原因优先于Foo 区域中指定的路由而不是Bar 中指定的路由地区。

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

然后,我的后续问题是:我如何指定/确定哪个区域将首先注册?如果这不是一个可靠的约定,是否有一种可接受的方式来声明这些路由,使得 url foo/bar/baz 将解析到我的 Bar 区域,而不必在 Global.asax 中声明它们?

【问题讨论】:

  • Phil Haack 有一个很棒的Route Debugger 可以让你知道你的路线去向。
  • 您可以粘贴您目前创建的路线列表吗?

标签: asp.net-mvc asp.net-mvc-routing


【解决方案1】:

在 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 非常适合解决此类问题。

【讨论】:

  • 通过在Global.asax 中声明两条路由,它似乎接受了第一个声明的路由。这样做似乎违背了将所有路线注册保持在指定区域内的目的。也许我通过“嵌套”区域所做的事情无论如何都需要我这样做?
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2017-12-30
    • 2011-03-17
    • 1970-01-01
    • 2010-12-09
    • 2018-05-31
    • 2011-08-07
    • 2017-02-21
    • 1970-01-01
    • 2021-02-13
    相关资源
    最近更新 更多