Область MVC asp.net маршрутизируется как часть другой области - PullRequest
0 голосов
/ 30 марта 2012

У меня есть область, Foo, которая имеет один сопоставленный маршрут: foo/{controller}/{action}/{id}.

У меня также есть другая область, Bar, которая по сути является подкомпонентом этой области.У него есть сопоставленный маршрут foo/bar/{controller}/{action}/{id}.

Так, например, у меня есть контроллер с именем BazController в моей области Bar, поэтому у меня может быть маршрут, который выглядит как foo/bar/baz.

Проблема в том, что маршруты для этого сценария не разрешаются, поскольку кажется, что мои сопоставленные маршруты ищут контроллер с именем BarController вместо сопоставления с маршрутом, объявленным с помощью foo/bar/{controller}/{action}/{id}

Я предполагаю, что есть некая фундаментальная концепция дизайна, которую я невольно нарушил ... если это так, как мне организовать это вместо двух областей?

Мое основное пониманиеURL-маршрутизация происходит из Django-фона, где вы можете делать такие вещи, как ссылки на отдельные URL-файлы, и все маршруты обрабатываются сверху вниз.Я не знаю, как приоритет отображения маршрутов определяется с помощью asp.net mvc, и не знаю, как упорядочить регистрацию маршрутов для областей.

ОБНОВЛЕНИЕ

Я использовал отладчик маршрута Фила Хаака, как и предложил @zLan, и он действительно совпадает на обоих моих отображенных маршрутах, и по какой-то причине имеет приоритет над маршрутом, указанным в области Foo, по сравнению с маршрутом, указанным в области Bar.

Я продолжил отладку и указал оба маршрута в Global.asax вместо соответствующих им RegisterArea методов, как предложил @mfanto, и кажется, что он выбирает маршрут, который был объявлен первым.

Мой следующий вопрос: как мне указать / определить, какая область будет зарегистрирована в первую очередь?И если это ненадежное соглашение, которому нужно следовать, есть ли приемлемый способ объявить эти маршруты таким образом, что URL foo/bar/baz будет преобразован в мою область Bar, без необходимости объявлять их все в Global.asax?

Ответы [ 2 ]

0 голосов
/ 03 апреля 2012

Кажется, проблема связана с порядком, в котором будут регистрироваться маршруты области. Поскольку я не нашел какой-либо убедительной документации о том, как сопоставляются маршруты области, и не думаю, что безопасно полагаться на какой-либо конкретный порядок, я продолжил и создал новый класс для инкапсуляции маршрутизации, которую я мог бы вызывать из другого файл маршрутов. По сути, это «модулирует» область, так что ее можно использовать в качестве набора маршрутов, добавляемых к другому префиксу url.

Основная идея состоит в том, чтобы эмулировать способность Джанго включать другие 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 голосов
/ 30 марта 2012

Работает ли для маршрута добавление чего-то вроде следующего в RegisterRoutes () в Global.asax?

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's Route Debugger отлично подходит для решения подобных проблем.

...