StoriKnow
StoriKnow

Reputation: 5866

MVC 5 Multiple Routes to Same Controller

In our RouteConfig.cs file we have the following default route:

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Original", action = "Index", id = UrlParameter.Optional },
                namespaces: new[] { "Path.To.Controllers" }
            );

Our application is split into several different "Areas". This particular route works perfectly fine.

We were asked to change one of our URLs, however the underlying codebase is the same. In an effort to avoid breaking existing links out there I'd like to setup my controller to support two different routes:

Here's an example of what the original URL looks like:

website.com/MyArea/Original

With the aforementioned "Default" route in place, this will be directed to the OriginalController in the MyArea Area, and will hit the default Index action since none was specified.

My goal is to setup another URL that will also direct itself to the OriginalController. That is,

website.com/MyArea/Other

Should route to the OriginalController in the MyArea Area, and hit the default Index action.

I've added the following to the RouteConfig.cs file:

            routes.MapRoute(
                name: "Other",
                url: "Other/{action}/{id}",
                defaults: new { controller = "Original", action = "Index", id = UrlParameter.Optional },
                namespaces: new[] { "Path.To.Controllers" }
            );

What I'm finding is that the Default route config is always used in favor of the Other route config, which causes a binding issue stating "OtherController could not be found". I suspect this is because they have a very similar signature in the url template, but I'm not entirely sure how to get around that.

I'm aware that there's a Route attribute also, which I'm not opposed to using. I unfortunately was unsuccessful in getting that setup correctly though.

After researching and attempting several different combinations I still can't get both URLs to route to one controller.

What am I doing wrong?

Upvotes: 0

Views: 4410

Answers (1)

StoriKnow
StoriKnow

Reputation: 5866

I was able to get the expected result using RouteAttribute on the controller itself (thank you @Michael for the resources and making me take another look at the RouteAttribute), rather than conventional MapConfig routing. As I described in my question above, I was having difficulties when attempting the Route approach in that I was receiving 404 errors stating the "resource could not be found".

It turns out the above errors were due to the fact that my attribute routing wasn't being wired up in the correct order, which forced the conventional route to be used (e.g. Default MapConfig) over my Route attributes.

I stumbled upon this SO post which states:

You are probably combining convention based routing with attribute routing, and you should register your areas after you map the attribute routes.

The line

AreaRegistration.RegisterAllAreas(); should be called AFTER this line:

routes.MapMvcAttributeRoutes();

When working with Areas, you must register those areas after you register the attribute routing. I was originally registering my areas in the Application_Start method of Globas.asax.cs, which is called before the RouteConfig.RegisterRoutes. I moved this registration to right below my MapMvcAttributeRoutes call in the RouteConfig.cs file, which allowed the following route attribute on the controller to work as expected:

[RouteArea("MyArea")]
[Route("Original/{action=index}", Order = 1)]
[Route("Other/{action=index}", Order = 0)]
public class OriginalController : Controller {
    ...
    ...
    public async Task<ActionResult> Index() { ... }
}

With the above in place, I can now navigate to either of the below URLs which will properly route to the "Index" action of my OriginalController:

website.com/MyArea/Original

website.com/MyArea/Other

This works. However, I do have another action defined that choked up the attribute routing and caused the conventional Default route (defined via the MapConfig function) to be used. My action signature:

public async Task<ActionResult> Details(int id) {
    ...
}

The route to this action is: website.com/MyArea/Original/Details/123, which also satisfies the default conventional route of {area}/{controller}/{action}/{id}.

The way around this was to go a step further with defining route attributes at the action level:

[Route("Original/Details/{id:int}")]
[Route("Other/Details/{id:int}")]
public async Task<ActionResult> Details(int id) {
    ...
}

Now my Route Attributes are found first, then the conventional route is used.

Upvotes: 3

Related Questions