Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to change order of routes in routing table when using attribute routing?

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.

like image 315
David T. Macknet Avatar asked Oct 15 '15 17:10

David T. Macknet


People also ask

What is the advantage of attribute routing over conventional routing?

Attribute routing provides you more control over the URIs by defining routes directly on actions and controllers in your ASP.NET MVC application and WEB API. To know about convention-based routing refer Routing in Asp.Net MVC with example.

How is attribute routing different from default routing?

As per our opinion, Attribute Routing provides us more flexibilities as compared to Convention Based Routing. In Attribute Routing, we can manage route as controller level, action level and also area level. Also, it can override the child route, if required.

How do you use attribute routing?

To enable Attribute Routing, we need to call the MapMvcAttributeRoutes method of the route collection class during configuration. We can also add a customized route within the same method. In this way we can combine Attribute Routing and convention-based routing. A route attribute is defined on top of an action method.

In what type of routing we need to specify the route attribute in the action method?

As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web application. The earlier style of routing, called convention-based routing, is still fully supported. In fact, you can combine both techniques in the same project.


2 Answers

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.

like image 189
Francesc Castells Avatar answered Sep 18 '22 01:09

Francesc Castells


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.

like image 31
Tomas Kubes Avatar answered Sep 21 '22 01:09

Tomas Kubes