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.
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With