I'm using default MVC routing setup:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
I have area defined as:
public class AdministrationAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Administration";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Administration_default",
"Administration/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
And I have a controller in that area:
namespace XXX.Areas.Administration.Controllers
{
public class CountryController : Controller
{
public ActionResult Index()
{
///
}
}
}
When I type
/Administration/Country
it works good as it is desired.
When I type
/Country
action still gets invoked, though view is not found so I get an error.
Why is MVC accepting
/Country
as valid route? I don't have another CountryController in non-area zone.
Add NameSpace in the Global.asax file for the default Route.
var route = routes.MapRoute(
"Default", // Route name
"{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Index" }, // Parameter defaults,
new[] { "YourNameSpace.Controllers" }
);
Add NameSpace in the AreaRegistration
class present in your Area
public class MyArea : AreaRegistration
{
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"test",
"Test/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new[] { "MyProjectNameSpace.Controllers" }
);
}
}
Explanation
I have a following area in my application. So the below highlighted section is out concerned Controller. ok.
Figure -1
I typed Url : http://localhost:2474/ActionFilterAttribute/index
Before moving toward the destination. I will show you some how I initialized my test. I added a Reference of RoureDebugger. You can get the Dll from this location. Then I added a line of code in my Global.asax file under Application_Start
Handler.
RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
So, finally using the above mentioned Url, I started debugging the application. Finally I saw the below picture.
Figure -2
Question
action still gets invoked, though view is not found so I get an error.
Answer
So if you pay attention to the above highlighted Route
, well, this is the Default Route
. This pattern is matched with Url as mentioned above. But View will not be found in this case and that's the reason your Controller Action Method
is Invoked.
Before moving to the next part that why did I get 404. I will show you some test I did in my sample application.
I created a class which derives from ActionFilterAttribute
like below. This contains only one Override
that's called OnResultExecuting
. This Handler executes before executing the View
corresponding to particular Action
The purpose of Creating this class is just to verify what is happening with RouteData
and DataTokens
.
public class MyActionFilter : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult != null)
{
var razorEngine = viewResult
.ViewEngineCollection
.OfType<RazorViewEngine>()
.Single();
var viewName = !String.IsNullOrEmpty(viewResult.ViewName) ?
viewResult.ViewName :
filterContext.RouteData.Values["action"].ToString();
var razorview = razorengine
.FindView
(
filtercontext.Controller.ControllerContext,
viewname,
viewResult.MasterName,
false
).View as RazorView;
}
base.OnResultExecuting(filterContext);
}
}
Your Controller
will be picked up by the default base route {controller}/{action}
before it checks the Area Route
and therefore will look for the View
in the Root/Views
instead of in the Area/views
.
To examine this, I set the debugger in the Action Method of Controller inside the Area and found that that there is no DataToken
Information when the Requested url is without Area Name
. let's see more details about DataToken
in Debug
Mode
Figure -3
If you pay attention to the ControllerContext
, I enumerated the DataTokens
, which is showing no key/Value. It's because no view is found pertaining to that controller under the Root Directory
How can you say that the currently located Directory is Root Directory? Proof is below
Figure -4
There is no Namespace or any Area mentioned in the RouteData
values. right?
Now let's move to the RouteData
that matched the pattern which is containing the Area Name
. So, this time my Url is : http://localhost:2474/mypractise/ActionFilterAttribute/index
and below is the RouteData
matched by URLRoutingModule
Figure -5
Please pay attention to the highlighted section, this time the Route matched belongs to AreaName
pattern and matched value is false for the Default Route which belongs to some RouteData
at Root Directory. Right?
My final details for the DataTokens
in case of above mentioned requested Url. You can see the Namespace details and Area details this time.
Figure -6
Conclusion :
When the Controller
is inside the Area and your DataTokens
are not showing the information for Area
, NameSpace
and UseNameSpaceFallback
informations. That means you will get 404. As mentioned in Figure-4, your requested Url was correct, so you got the DataTokens
and As mentioned in Figure 3, DataTokens were not shown because the requested Url does not contains the Area Name
and despite of the fact that you have got the RouteData
as mentioned in Figure 2 because it's a default Url Pattern. Finally try to execute the third line of code in OnResultExecuting
. It will show null because View is not found.
Hope this explanation will help you.
Check it. Modify the default route in your Global.ascx.cs file like so.
var route = routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "APPLICATION_NAMESPACE.Controllers.*" }
);
route.DataTokens["UseNamespaceFallback"] = false;
EDIT:
My apologies. It seemed like you didn't want it to do this as well as know why.
You are running into the fact that the default routing will look for anything that is a controller. Even if it's in an Area. You can overcome this default behavior by simply adding the namespaces
parameter to the route and specify what the default routing should be looking for with controllers.
The solution that I provided above is merely a fix if you wanted to not serve the view of an area outside the area itself.
There is a great article on why this is occurring here.
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