0

My MVC application is set up with controllers at the root/global level. These have no explicit Area. I also have a single "Admin" area. URLs that begin with /admin/ are routed to the matching controller in the Admin area. Other URLs are routed to the matching global controller. This is working fine for the most part.

The issue I'm having is that in the case when a URL matches a controller in the Admin area when one of the same name doesn't exist on the global area, the request is incorrectly routed to the controller in Admin area. I know this is happening because I put a breakpoint on the matching action in the relevant controller.

For example, I have a controller called CalendarController in the Admin area. When I visit /admin/calendar, it works because it finds the action and the corresponding view in Areas/Admin/Views/Calendar/Index.cshtml. The problem occurs when I visit /calendar. I do not have a controller named Calendar at the root level, but for some reason it routes the request to the Admin area's CalendarController, which I don't want. I want it to return a 404 because no CalendarController exists at the root level. Instead, I get an error because it's searching for the view at the root level (at /Views/Calendar/Index.cshtml) even though the matching controller was in the Admin area.

How can I prevent the Admin area from being searched for matching controllers except when the URL has /admin in it?

Here's the relevant route code, which is basically stock except for the namespaces addition. The issue still happens without the namespace. There are more routes in the actual application, but I'm getting the same behavior in a brand new MVC project.

public class AdminAreaRegistration : AreaRegistration 
{
    public override string AreaName 
    {
        get 
        {
            return "Admin";
        }
    }

    public override void RegisterArea(AreaRegistrationContext context) 
    {
        context.MapRoute(
            "Admin_default",
            "Admin/{controller}/{action}/{id}",
            new { action = "Index", id = UrlParameter.Optional },
            new[] { "AreaProblem.Areas.Admin.Controllers" }
        );
    }
}

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Error page

What I mean by a controller at the "root level" is Controllers/HomeController in this screenshot. I want URLs that don't start with /admin to only look at those controllers. The problem is that it's also searching in Areas/Admin/Controllers.

Solution explorer

Sam
  • 4,994
  • 4
  • 30
  • 37
  • 1
    Nevermind! You get this message because the server couldn't find Index anywhere. – Auguste Oct 30 '15 at 20:43
  • @Auguste - that's not the point of the question – Igor Oct 30 '15 at 20:44
  • @Auguste Not all of my Admin controllers have matching ones at the root level, so I would have to make a bunch of dummy controllers for that to work. Unless the URL starts with admin/, I want it to just search the controllers at the root level and give up if it doesn't find a match, not continue on and search in the areas. – Sam Oct 30 '15 at 20:46
  • @Sam - You might find your answer here: http://stackoverflow.com/questions/632964/can-i-specify-a-custom-location-to-search-for-views-in-asp-net-mvc – Vinay Oct 30 '15 at 20:54
  • @Vinay thanks, I'll take a look. I also added a screenshot to clarify the structure of the project. Edit: looked at that question and it's not quite what I want. I don't care that it's searching for the calendar's Index view in the wrong folder because I don't want it to be routed to that controller in the first place. – Sam Oct 30 '15 at 20:56
  • I think I know what could be wrong. Are both of your controllers in the same namespace? – Becuzz Oct 30 '15 at 21:02
  • @Becuzz no, CalendarController is "AreaProblem.Areas.Admin.Controllers" and HomeController is "AreaProblem.Controllers" – Sam Oct 30 '15 at 21:04
  • @Becuzz - there is no second controller - "There is no spoon." – Igor Oct 30 '15 at 21:05
  • Right, there are not two CalendarControllers. But the namespaces are different for all the Admin area controllers – Sam Oct 30 '15 at 21:05
  • Awesome answer below. I have abandoned the MVC default stuff completely. Better to just use the Route attribute. Have you looked into that? – Mr. B Oct 30 '15 at 21:20
  • @Mr.B Yeah, I hate MVC routing. The route attribute is way better, but I didn't think there was a way to use the route attribute outside of Web API 2 and MVC 6. Is that not the case? – Sam Oct 30 '15 at 21:23
  • 1
    @Sam Pretty sure its there in MVC 5, but this little Gem has been around since MVC 3 https://www.nuget.org/packages/AttributeRouting/ – Mr. B Oct 30 '15 at 21:26
  • @Mr.B yep, you're right... I've been wishing I could use that ever since coming back to MVC after a couple Web API 2 projects but never thought to actually check if it was there. thanks – Sam Oct 30 '15 at 21:31

1 Answers1

5

So the MVC routing engine will look in different namespaces to try and find a matching controller. You can solve this by specifying a namespace (like you did for the admin area). You can also specify that a route not search other namespaces using DataTokens.

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    namespaces: new [] { "AreaProblem.Controllers" }
    ).DataTokens["UseNamespaceFallback"] = false;
Becuzz
  • 6,846
  • 26
  • 39