Duane
Duane

Reputation: 710

ASP.NET MVC3 Area controller accessible from global routes?

Perhaps I do not understand correctly how MVC Areas work, but this has got me a little confused.

  1. Add an Area called "MyArea" using right-click "Add Area" in Visual Studio on the MVC3 project
  2. Create a controller for MyArea: "AnArea" with matching view in the MyArea area.
  3. Add "controller = "AnArea" to context.MapRoute's defaults parameter in MyAreaAreaRegistration.RegisterArea method.

So at this point if you start the application and navigate to /MyArea/ it should load the AnArea controller with it's matching view. If you navigate to /MyArea/AnArea, it will show the same result.

But, if you navigate to /AnArea/, the controller is still found and the following error message is displayed:

The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/anarea/Index.aspx
~/Views/anarea/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Views/anarea/Index.cshtml
~/Views/anarea/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml

Is this the correct behaviour? I would have thought an area's controller could only be accessed via it's own area and not globally.

Upvotes: 5

Views: 1681

Answers (3)

Darin Dimitrov
Darin Dimitrov

Reputation: 1039160

You seem to be navigating to /AnArea whereas your area is called MyArea so you should navigate to /MyArea/. Here's how the area route registration looks like:

context.MapRoute(
    "MyArea_default",
    "MyArea/{controller}/{action}/{id}",
    new { controller = "AnArea", action = "Index", id = UrlParameter.Optional }
);

AnArea is the name of the controller, not the area. If you want to navigate to some controller of this area you should always prefix your url with MyArea which is the name of the area.

Upvotes: 0

counsellorben
counsellorben

Reputation: 10924

Whenever I create an project with areas, I change my Default route as follows:

    routes.MapRoute( 
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // defaults
        null,  // constraints
        new string[] { "MyApplication.Controllers" } // namespaces
    );

The final parameter limits the default route to the controllers in the MyApplication.Controllers namespace. This insures that the Default route is limited to actions outside of any areas.

UPDATE

After a deep dive into the code, I discovered where the issue arises, and have a solution. Change your Default route to the following:

routes.Add(
    "Default", 
    new Route("{controller}/{action}/{id}",
        new RouteValueDictionary(
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        ),
        null,
        new RouteValueDictionary(
            new {
                Namespaces = new string[] { "MyApplication.Controllers" },
                UseNamespaceFallback = false 
            }
        ),
        new MvcRouteHandler()
    )
);

The key is in adding the UseNamespaceFallback token. This will prevent the Default route from looking into any other namespaces.

This is unexpected behavior, and it was a problem I was unaware of which affects a project I am working on. I will list it as an issue at aspnet.codeplex.com. I would not call this a bug, but the behavior definitely appears to breach the convetions for MVC routing.

Upvotes: 6

Lelis718
Lelis718

Reputation: 635

You have to apply a namespace restriction in both area and general route.

In global.asax.cs you should edit RegisterRoutes method just like this

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 }, 
      new string[] { "MyProject.Controllers" }
   );
}

That will restrict "//" only to the namespace "MyProject.Controllers"

But also you´ll have to apply the namespace restriction to the Area to restrict "//" only to the namespace "MyProject.Areas.MyArea.Controllers"

For that you´ll have to edit "RegisterArea" method of "MyAreaAreaRegistration.cs" like below ("MyAreaRegistration.cs" is located at "/MyProject/Areas/MyArea" folder ) :

//Some default code stuff
...
public override void RegisterArea(AreaRegistrationContext context)
{
   context.MapRoute(
      "MyArea_default",
      "MyArea/{controller}/{action}/{id}",
      new { action = "Index", id = UrlParameter.Optional },
      new string[] { "MyProject.Areas.MyArea.Controllers" }
   );
}

Hope it helps!!

Upvotes: 0

Related Questions