Можно ли изменить порядок маршрутов в таблице маршрутизации при использовании маршрутизации атрибутов?

Итак, я переключаю область с помощью AreaRegistration на использование Routing Attribute. Я столкнулся с проблемой, которая, по-видимому, вызвана порядком загрузки маршрутов в таблицу маршрутизации. Я решил проблему в AreaRegistration, загрузив ее в проблемный маршрут последним, так что только если все остальные маршруты не совпадают, этот маршрут будет согласован. При использовании маршрутизации атрибутов это не представляется возможным. У меня есть параметр Order при создании маршрута, но это не влияет на то, как вещи попадают в таблицу маршрутизации, но очень узко.

Здесь маршрут, который у меня есть в файле AreaRegistration:

context.MapRoute(
    name: "ActionItems_home",
    url: "ActionItems/{group}/{statuses}/{overdueOnly}",
    defaults: new { controller = "Home", action = "Index", group = "All", statuses = "New,Open", overdueOnly = false },
    namespaces: new string[] { "IssueTracker.Areas.ActionItems.Controllers" }
    );

Теперь, когда я пытаюсь переключить это на Маршрутизацию атрибутов, единственное, что подходит для работы, это:

[Route("", Order = 4)]
[Route("{group:regex(^(?!Item|DecisionLogs))?}", Order = 3)]
[Route("{group:regex(^(?!Item|DecisionLogs))}/{statuses=New,Open?}", Order = 2)]
[Route("{group:regex(^(?!Item|DecisionLogs))}/{statuses=New,Open}/{overdueOnly:bool=false?}", Order = 1)]

Обратите внимание, что мне нужно добавить регулярное выражение, потому что иначе контроллер элемента не будет вызван - вместо этого я получаю строку "Item", которая передается как параметр group. Но регулярное выражение не особенно помогает в том, как отображаться URL-адрес.

Я хотел бы, чтобы необязательные параметры были подавлены в URL-адресе, если они не являются стандартными. Я попытался указать параметры как необязательные, со значениями по умолчанию, и как необязательные, так и со значениями по умолчанию. Ни один из них, похоже, действительно не делает трюк.

Текущее решение по крайней мере представляет URL-адрес без запроса, но они включают в себя необязательные параметры и делают вещи уродливыми. На данный момент я просто оставил вопиющие маршруты, которые будут определены в файлах AreaRegistration, и не украсил их фрагментами [Route()].

Ответы

Ответ 1

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

public class OptionalsRouteAttribute : RouteFactoryAttribute
{
    private object _defaults;

    public OptionalsRouteAttribute(string template, object defaults)
        : base(template)
    {
        Defaults = defaults;
    }

    [...]
}

Здесь вы можете увидеть образец И оригинальный источник RouteFactoryAttribute для справки

Я боюсь, что у меня нет времени прямо сейчас, чтобы обеспечить реальную реализацию, но я надеюсь, что это приведет вас в правильном направлении.

UPDATE Я дал эту попытку, и следующее очень простое решение работает так, как ожидалось. Моя реализация атрибута специфична для образца, который вы предоставили с параметрами группы, статусов и просроченной только, но вы должны иметь возможность создавать более общее решение, которое охватывает все ваши случаи (вам также необходимо добавить пространство имен).

 public class OptionalsRouteAttribute : RouteFactoryAttribute
{
    public OptionalsRouteAttribute(string template, string group, string statuses, bool overdueOnly)
        : base(template)
    {
        var defaults = new RouteValueDictionary
        {
            {"group", @group},
            {"statuses", statuses},
            {"overdueOnly", overdueOnly}
        };
        Defaults = defaults;
    }

    public override RouteValueDictionary Defaults { get; }

}

Затем в контроллере:

 [OptionalsRoute("ActionItemsAttribute/{group}/{statuses}/{overdueOnly}", "All", "New,Open", false)]
    public ActionResult AttributeRouting(string group, string statuses, bool overdueOnly)
    {
        ViewBag.Message = $"Attribute Routing: Group [{@group}] - Statuses [{statuses}] - overdueOnly [{overdueOnly}]";
        return View("Index");
    }

И он работает точно так же, как и исходная конфигурация маршрутизации, но использует атрибут.

Ответ 2

ASP.NET MVC является открытым исходным кодом, и он разработан таким образом, что каждый уровень может быть заменен вашим собственным. Вы можете загрузить исходный код, найти проблемную часть и узнать, как она работает. Наконец, замените его собственным кодом.

git clone https://git01.codeplex.com/aspnetwebstack.git

Если вы загрузите этот исходный код, вы найдете класс System.Web.Mvc.RouteAttribute с методом CreateRoute. Я не уверен, но это может быть как-то связано с вашей проблемой.

RouteEntry IDirectRouteFactory.CreateRoute(DirectRouteFactoryContext context)
{
    Contract.Assert(context != null);

    IDirectRouteBuilder builder = context.CreateBuilder(Template);
    Contract.Assert(builder != null);

    builder.Name = Name;
    builder.Order = Order;
    return builder.Build();
}

Поиск ссылок на этом интерфейсе можно найти в классе DefaultDirectRouteProvider.

Другой подход заключается в том, что вы можете попытаться найти работу "Заказ" в проекте. Если вы пропустите тест, не так много вхождений, а некоторые из них находятся в классах классов.

Вы можете снять флажок "Только мой код" в Visual Studio и отладить код ASP.NET MVC.