Is it possible to change order of routes in routin

2019-06-15 01:53发布

So, I'm switching an area over from using AreaRegistration to using Attribute Routing. I'm running into an issue which appears to be caused by the order in which routes are loaded into the routing table. I'd solved the issue in AreaRegistration by loading in the problematic route last, so that only if all other routes didn't match would that route be matched. With Attribute Routing, this doesn't appear to be possible. I have the Order parameter when creating a route, but this doesn't affect how things hit the routing table except very narrowly.

Here's the route I have in the AreaRegistration file:

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" }
    );

Now, when I try to switch this to Attribute Routing the only thing that comes close to working is:

[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)]

Note that I have to put in the regex because otherwise the Item controller doesn't get called - instead, I end up with the string 'Item' being passed in as the group parameter. But the regex doesn't particularly help with how the URL's end up being rendered.

I would like for the optional parameters to be suppressed in the URL unless they are non-default. I've tried specifying the parameters as optional, with default values, and both optional and with default values. None of them seems to really do the trick.

The current solution at least presents a URL without a querystring, but they include the optional parameters and make things ugly. For now, I've simply left the egregious routes to be defined in AreaRegistration files & not decorated them with the [Route()] pieces.

2条回答
聊天终结者
2楼-- · 2019-06-15 02:21

ASP.NET MVC is open source and it is designed in the way that every layer can be replaced by your own. You can download the source code, find problematic part and find out, how it works. Finally replace by own code if needed.

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

If you download this source code, you will find a System.Web.Mvc.RouteAttribute class with CreateRoute method. I am not sure, but it could be somehow connected with your problem.

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();
}

Finding references on this interface, you can find DefaultDirectRouteProvider class.

Another approach is that you can try to find work "Order" in the project. If you omit test, there are not so many occurrences and some are in routeSomething classes.

You can uncheck "Just my code" in Visual Studio and debug the ASP.NET MVC code.

查看更多
等我变得足够好
3楼-- · 2019-06-15 02:25

Your real problem is how to configure your original route with Attribute Routing. The order problem is just a side effect of configuring several routes instead of one. To achieve your desired configuration, you can create a custom RouteAttribute and do whatever you need inside.

public class OptionalsRouteAttribute : RouteFactoryAttribute
{
    private object _defaults;

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

    [...]
}

You can see a sample here And the original RouteFactoryAttribute source for reference

I'm afraid I don't have time right now to provide the actual implementation myself, but I hope this will lead you to the right direction.

UPDATE I've given this a try and the following very simple solution works as expected. My attribute implementation is specific to the sample you provided with group, statuses and overdueOnly parameters, but you should be able to create a more generic solution that covers all your cases (you'll also need to add the namespace)

 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; }

}

Then in the Controller:

 [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");
    }

And it works exactly the same as your initial routing configuration, but using an attribute.

查看更多
登录 后发表回答