MVC Default Area Not working - c#

I have a web site that had no areas registered. Then I registered an area called "MyNewArea".
Now my default website links like blogs etc no longer work.
So I now have an areas folder with a single area in it and the default folders when I created the project in the first place.
In my area AreaRegistration class I have;
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"MyArea_default",
"{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
but this looks like it conflicts with the default one of
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
What do I need to do to get the area to work with the default site and controllers?

You're right, that mapped route would conflict (in the sense of "conflict" as in it will be matched first). You would need to alter your mapped Area route to be something like:
context.MapRoute(
"MyArea_default",
"MyArea/{controller}/{action}/{id}",
new { controller = "MyAreaController", action = "Index", id = UrlParameter.Optional }
);
The reason why your URLs broke after you added this Area (and in turn the Area route), it used your Area route for handling that didn't exist in your MyArea Area.

Change the new area route table to be:
context.MapRoute(
"MyArea_default",
"MyNewArea/{action}/{id}",
new { controller = "MyNewArea", action = "Index", id = UrlParameter.Optional }
);

Related

How to add some Text before {controller}/{action}/{id} asp.net mvc

I want to change this
localhosht:3220/MyController/MyAction?id=1
to this
localhosht:3220/myText/MyController/MyAction?id=1
So that it works any time, even in redirection, routing or other cases.
Thanks.
Just add the text to the route url:
routes.MapRoute(
name: "Default",
url: "myText/{controller}/{action}"
);
You can do it with a custom route to match your desired uri. In the RegisterRoutes method of RouteConfig, add the following.
The order is important here, if you add the "Default" route before the "MyText" route, then MyText route will not be hit.
//for your custom route that starts with "myText"
routes.MapRoute(
name: "MyText",
url: "myText/{controller}/{action}/{id}",
defaults: new { controller = "MyController", action = "MyAction", id = UrlParameter.Optional }
);
//for other normal routes
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
While adding a route with the url parameter of myText/{controller}/{action}/{id} (above your default route) will certainly work, if your intention is to use the myText frequently, you might also want to take a look at the concept of areas.
By default, in your Global.asax, in your Application_Start() function, you have a call for the method:
AreaRegistration.RegisterAllAreas();
What this does is to look for a special Areas folder in your project, and register each folder under it that has a class that extends from AreaRegistration and overrides the RegisterArea function. Similar to your RouteConfig.cs, this class can look like this:
public class MyTextAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "MyText";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
name: "MyText_default",
url: "MyText/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
note: you can create Areas in your MVC project by scaffolding: Just right click on your project and Add.. -> Area. Everything will be done for you automatically.
In the end, Areas provide a more permanent solution to your MyText/SomeController/SomeAction needs. For a detailed article, you can check this.
routes.MapRoute(
name: "Default",
url: "{myText}/{controller}/{action}/{id}"
defaults: new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional,
myText= UrlParameter.Optional
);
It will work for you if you want to use parameter like myText to make your Url Seo friendly.

MVC route matching routes that aren't valid

Problem
For the default route MVC is returning a "Multiple types were found that match the controller named 'admin'." error instead of a 404 not found. There is no admin controller in that namespace.
We're using MVC 5.2.2.
Background
We're using MVC areas. Two areas contain an "admin" controller. When you use the full path as defined in their respective routes, both areas' admin controllers are accessible and work correctly.
The problem arises when you try to access "admin" from the default route. Admin does not exist in that context, so we'd expect a 404 not found, however instead we receive:
Multiple types were found that match the controller named 'admin'. This can happen
if the route that services this request ('{controller}/{action}/{id}') does not
specify namespaces to search for a controller that matches the request. If this
is the case, register this route by calling an overload of the 'MapRoute' method
that takes a 'namespaces' parameter.
The request for 'admin' has found the following matching controllers:
DMP.Stock.Web.Controllers.AdminController
DMP.Inventory.Web.Controllers.AdminController
Here is our default route and two area routes:
public static void RegisterRoutes(RouteCollection routes)
{
// Default Route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "DMP.Core.Web.Controllers" }
);
}
public override void RegisterArea(AreaRegistrationContext context)
{
// Area 1: Stock
context.MapRoute(
name: "Stock_default",
url: "Stock/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "DMP.Stock.Web.Controllers" }
);
}
public override void RegisterArea(AreaRegistrationContext context)
{
// Area 2: Inventory
context.MapRoute(
"Inventory_default",
"Inventory/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "DMP.Inventory.Web.Controllers" }
);
}
/Stock/Admin/Index works correctly.
/Inventory/Admin/Index works correctly.
/Admin/ does not work correctly (expect: 404 not found, receive "multiple controllers" error).
The errors suggests we add namespaces to our routes, but as you can see above both the default and the two areas already have a namespace definition. The default route points at a namespace without any "admin" controllers in it.
I think MVC is trying to be "helpful" by searching for possible controllers that could match the requested URL. Is there any way I can turn that off?
I've been able to resolve this issue myself. Here is the solution I found:
// Default Route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "DMP.Core.Web.Controllers" }
).DataTokens["UseNamespaceFallback"] = false;
Note the addition of .DataTokens["UseNamespaceFallback"] = false; this is what resolves the issue. There isn't very much (any?) documentation on this functionality, however I found it while reading the MVC source code, specifically within DefaultControllerfactory (the source of this issue).
After you know to google for "UseNamespaceFallback" you can find a few blog posts and questions where people have had a similar issue and resolved it the same way. However I can find no MSDN documentation on this DataToken.

MVC controllers with same name with areas but finds "Multiple types"

I have myself a little problem with areas and controllers.
I have a controller called "HomeController" this is the default controller, I then added a new area called "Supplier", I then copied over my HomeController and removed all the methods I don't want to edit and just edited my Index method.
Now when I build it works fine but when I go to my home controller as a supplier it comes up with this error
Multiple types were found that match the controller named 'Home'. This can
happen if the route that services this request ('{controller}/{action}/{id}')
does not specify namespaces to search for a controller that matches the request.
If this is the case, register this route by calling an overload of the
'MapRoute' method that takes a 'namespaces' parameter.
The request for 'Home' has found the following matching controllers:
TestProject.Controllers.Home
TestProject.Areas.Supplier.Controllers.Home
I have updated my areas like so
This is my default area
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "TestProject.Controllers" }
);
}
}
And here is my area route file
public class SupplierAreaRegistration: AreaRegistration
{
public override string AreaName
{
get
{
return "Supplier";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"SupplierHomeIndex",
"Home/Index/{id}",
defaults: new { area = "Supplier", controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "TestProject.Areas.Supplier.Controllers" },
constraints: new { permalink = new AreaConstraint(AreaName) }
);
context.MapRoute(
"SupplierDefault",
"{controller}/{action}/{id}",
defaults: new { area = "Supplier", action = "index", id = UrlParameter.Optional },
namespaces: new[] { "TestProject.Controllers"},
constraints: new { permalink = new AreaConstraint(AreaName) }
);
}
}
Can anyone sign some light on this? I have looked at many topics and answers for this via Google and Stackoverflow however nothing seems to work for me.
You've customized the area's routes and removed the Supplier URL prefix. When the routing framework spins up it merely collects all controllers from your application, regardless of where they are, and then looks for a match based on the URL. In your case, you now have two controllers that are both bound to the URL /Home/*. Typically, the area's URL would be prefixed with the area's name to avoid the confusion, i.e. /Supplier/Home.

Using RouteLink with Areas

In my MVC application I have a default route defined:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new []{ "Demo.Controllers" }
);
I created a new Area called Admin and it added a route to in the AdminAreaRegistration class:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", controller = "Home", id = UrlParameter.Optional }
);
}
In my main _Layout file I tried to do the following:
#Html.RouteLink("Admin", "Admin_default")
It only works in certain cases (such as if I'm already in an Admin page). If I am in the /Home/About section of my site, then the URL gets generated like so:
/Admin/Home/About
If I am in my Index action of the Home controller (in the main area, not admin) then the URL gets generated like so:
/Admin
Why doesn't RouteLink work like I think it should using Areas in MVC?
#Html.RouteLink("Admin", "Admin_default") this route uses the route with the name of Admin_default so it will always use that route to generate the url.
#Html.RouteLink("Admin", "Admin_default", new { controller = "Home", action = "Index" })
When you don't specify stuff like route values most of MVC uses the values that currently exist. In this case since the action and controller values are null it looks at the RouteValues and checks to see if they're there and if they're found they use the values found there instead.
This is sometimes helpful. For instance if you want {id} to be populated with the same value that was used on the page.
Url: /home/edit/1701
#Html.RouteLink("Refresh", "Default") would result in the same exact url - you can specify overrides if you want.
I would also suggest not naming your routes. You can force yourself to do this by passing null to the route name arg when creating it.
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(null,
"Admin/{controller}/{action}/{id}",
new { action = "Index", controller = "Home", id = UrlParameter.Optional }
);
}
You can then use RouteLink like so:
#Html.RouteLink("Admin",
new { controller = "Home", action = "Index", area = "Admin" })
Reply to comment
Wish I could say I came up with the idea. Learned this from Steve Sanderson's MVC book. There are a ton of method overloads that take a route name as a parameter (RouteLink, but also RedirectToRoute and others). Not using route names forces you to use other overloads of those methods, which IMO are more intuitive and easier to "get right".
Naming your routes is not recommended since it creates dependencies in your view towards the existing routes. I suggest using a null value

Is it possible, in MVC3, to have the same controller name in different areas?

In MVC3, I have the following areas:
Mobile
Sandbox
Then i route maps like this:
context.MapRoute(
"Sandbox_default",
"Sandbox/{controller}/{action}/{id}",
new { controller = "SandboxHome", action = "Index", id = UrlParameter.Optional }
and
context.MapRoute(
"Mobile_default",
"Mobile/{controller}/{action}/{id}",
new { controller = "MobileHome", action = "Index", id = UrlParameter.Optional }
);
The problem is this gives urls like:
http://localhost:58784/Mobile/MobileHome
and
http://localhost:58784/Sandbox/SandboxHome
But I want it like this:
http://localhost:58784/Mobile/Home
http://localhost:58784/Sandbox/Home
The problem is when I rename the SandboxHome-Controller to Home, and the MobileHome-Controller to Home, which would give the desired URLs, it won't compile, saying it has two classes for HomeController.
How can I have the same controller name in different areas ?
Yes.
As explained by this blog post: http://haacked.com/archive/2010/01/12/ambiguous-controller-names.aspx
Assuming you have a call to RegisterAllAreas and the AreaRegistration files generated by Visual Studio. All you need to do is the namespace on the default route in global ASAX to prevent conflicts.
//Map routes for the main site. This specifies a namespace so that areas can have controllers with the same name
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[]{"MyProject.Web.Controllers"}
);
As long as you keep the Area controllers within their own namespaces. This will work.
Yes it is but you'll have to change your routing:
context.MapRoute(
"Default",
"{area}/{controller}/{action}/{id}",
new { area = "Mobile", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You could as well keep both routes but don't forget to define area in your defaults.
Important
Of course you must keep controllers in their own area namespaces:
namespace MyApp.Areas.Mobile.Controllers
{
public class HomeController : Controller
{
...
}
}
namespace MyApp.Areas.Sandbox.Controllers
{
public class HomeController : Controller
{
...
}
}
Check this link on MSDN and see the walktrough. And don't forget to also check out this MSDN article that talks about area registration, because you will have to call RegisterAllAreas() method.
And since you still want to keep original non-area controllers, you should also read this Phil Haack's article how to do just that (Credit should go to #Rob in his answer for pointing to this blog post first).

Categories

Resources