3
votes

I have some area in my ASP.NET MVC3 Application:

namespace MyProject.Areas.myarea
{
    public class myareaAreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get
            {
                return "myarea";
            }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "myarea_default",
                "myarea/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

this area contains "Hello" controller with "Smile" action.

In global.asax file for whole project I have:

    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 }
        );

    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    }

So, when I request "localhost/myarea/hello/smile" it calls appropriate controller as expected.

BUT! When I request "localhost/hello/smile" it calls hello controller STILL! With that, it looks for the Views not in the myarea/Views folder, but in a ~/Views folder for "root" (non-area) level of project.

How can I fix this, so server will throw an 404 exception, that resource is not found, just like I requested non-existing controller?

UPD: Controllers in area are in namespace:

namespace MyProject.Areas.myarea.Controllers
{
    public class HelloController : Controller

...
}

Controllers in "root"-level are in namespace:

namespace MyProject.Controllers
{
    public class AnotherRootController : Controller
...
}

So I tried this in global.asax:

    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 [] { "MyProject.Controllers" } //Namespace
        );

    }

I thought this would restrict this route to "root"-level controllers only, since they are in MyProject.Controllers namespace. That DID NOT work. Area-controllers are still being called with request without areaname in it.

May be someone can explain, why?

1
In MVC4 "Default" route declaraton moved from Global.asax to ~/App_Start/RouteConfig.cs/RegisterRoutes()Andriy F.

1 Answers

7
votes

You could set the UseNamespaceFallback=false datatoken when registering the default route in your Global.asax by restricting it to look only for controllers in the given namespace. You may take a look at the following blog post.

So to put that into action add a namespace restriction to your area registration:

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

and in your Global.asax set the UseNamespaceFallback data token to false when registering the default route in order to constrain it to the given namespace:

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[] { "MyProject.Controllers" }
    ).DataTokens["UseNamespaceFallback"] = false;
}