I have placed all my admin controllers inside an Admin folder in Controller folder. Since one of my admin controller matches the name of another controller in Controller folder, I am getting the following error.
Multiple types were found that match the controller named 'Product'. 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.
I tried adding the following route, but still the problem is same
routes.MapRoute(
"", //Route name
"Admin/{controller}/{action}/{id}", // Url with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
How can i get rid of this error. Changing the name of controller is the last option on my mind. Right now, I am looking for a way to preserve the name and see if i can find another way around this.
I have placed all my admin controllers inside an Admin folder in Controller folder
Well, that's your problem. How do you expect the default controller factory to know which controller you want to be instantiated given the following request /admin/index (the one in the Controllers folder or the on in the Controllers/Admin folder)? Remember that the default controller factory searches for types in the loaded assemblies that derive from Controller. It doesn't really care in which folder they were declared. So when it finds that you have 2 controllers with the same name it doesn't know which one to pick.
One possibility is to use Areas. Then you could specify namespaces when registering the route:
routes.MapRoute(
"",
"Admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "AppName.Areas.Admin.Controllers" }
);
Also in your Global.asax make sure that you specify the namespace for the non-area controllers:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "AppName.Controllers" }
);
Related
I'm building a web application (ASP.NET MVC 5) with a custom admin section where all the parameters of the apps are.
I want to be able to easily change the name of this section.
E.g.
myapp.com/admin/{controller}/{action}
could be
myapp.com/custom-admin-name/{controller}/{action}
I've tried to use areas but it seems like it would be hard to edit their name since all the controllers and models are bound to the area's namespace.
I've also tried to set custom routes
routes.MapRoute(
"AdminControllerAction",
"custom-admin-name/{controller}/{action}",
new { controller = "Dashboard", action = "Index" }
);
So I could do
mywebsite.com/custom-admin-name/dashboard/index
But the problem with that is that my admin controllers and actions are still callable using
mywebsite.com/dashboard/index
Is it possible to cancel the default routing of a controller/action ?
Is there any more viable solutions to this problem that I wouldn't have thought about ?
There is a way to restrict the controller namespaces for a given route, so controllers which don't belong to those namespaces will be ignored.
For example, the following will be restricted to controllers in the namespace YourApp.Controllers (You can add multiple namespaces if needed):
Route route = routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "YourApp.Controllers" }
);
route.DataTokens["UseNamespaceFallback"] = false;
Disabling the namespace fallback is important, otherwise you will just be prioritizing those namespaces.
So, you could restrict the default route to the namespace YourApp.Controllers as above and create a custom admin route restricted to the namespace YourApp.Controllers.Admin:
Route route = routes.MapRoute(
"AdminControllerAction",
"custom-admin-name/{controller}/{action}",
new { controller = "Dashboard", action = "Index" },
namespaces: new[] { "YourApp.Controllers.Admin" }
);
route.DataTokens["UseNamespaceFallback"] = false;
Please note that as mentioned by Tareck, the admin route has to be defined before the general route.
Try removing with this
RouteTable.Routes.Remove(RouteTable.Routes["NAME ROUTE YOU WISH TO RMOVE"]);
I need to model routes that act a little strange to me. Here is what they would like me to do:
/AssetManagement -> Asset Landing page
/AssetManagement/Add -> Add an asset
/AssetManagement/Edit -> Edit an asset
/AssetManagement/Locations -> Locations landing page
/AssetManagement/Locations/Add -> Add a location
/AssetManagement/Locations/Edit -> Edit a location
I'm not sure, but I think this needs to be modeled with two controllers. AssetsController and LocationsController. I think the views Add/Edit/Index would exist under the respected View folders and I would probably have two routes defined:
routes.MapRoute(
"AssetManagement", // Route name
"AssetManagement/{action}/{id}", // URL with parameters
new { controller = "Assets", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new[] { "MyApplication.Web.Website.Controllers" }
);
routes.MapRoute(
"AssetManagementLocations", // Route name
"AssetManagement/{controller}/{action}/{id}", // URL with parameters
new { controller = "Locations", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new[] { "MyApplication.Web.Website.Controllers" }
);
It just feels a bit wrong to me. Am I missing a best practice on handling stuff? What kinds of problems could this cause down the road? A lot of the system they want me to build works like this.
// Edit
If this is a bad place to ask questions like this, where should I ask them?
What you can do is create a Area.
In the area create 2 controllers one for asset and other for Location.
Instead of playing with routes(which can be dangerous) play with the MVC concept.
The 2nd Route you have specified closely relates to Areas.
When you create the area the route is automatically created for you.
hope this helps.
Area - AssetManagement
Controller1 - HomeController
Action1 - Add
Action2 - Delete
Controller2 - LocationsController
Action1 - Add
Action2 - Delete
routes.MapRoute(
"AssetManagementLocations", // Route name
"{Area}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Locations", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new[] { "MyApplication.Web.Website.Controllers" }
);
Check out the attributerouting project. You can Nuget install into mvc and decorate your action methods or controllers with the path- this leads to clear intention of exactly which routes map to which controller/method
https://github.com/mccalltd/AttributeRouting/wiki/2.-Usage
I'm attempting to do a custom route so I can prefix a url in my application with a chosen string and the do some processing based on that. The problem I'm running into is, that the action links that are generated are not contextualized based on the url that it exists on.
Routes:
routes.MapRoute(
"TestRoute",
"TEST/{controller}/{action}/{id}",
new { controller = "Space", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Space", action = "Index", id = UrlParameter.Optional });
Navigating to TEST/Space/Index works, as well as Space/Index, but the odd issue I need fixed is that the links generated via ActionLink do not obey the context in which they are loaded, at least for the {controller}/{action}/{id} default route. Pages that are loaded under TEST/Space/Index list links properly, but when /Space/Index is loaded, they are all referencing the TEST/ route that the calling url does not. Is this the default behavior? Is there a way to get these links to generate in the proper context?
Edit:
The first place I saw this was in the Html.BeginForm without the TEST/
Html.BeginForm("ToTheMoon", "Space", FormMethod.Post)
which renders the link as TEST/Space/ToTheMoon
but it also shows up in links:
#Html.ActionLink("Take Me To The Space Port", "SpacePort", "Space")
which renders TEST/Space/SpacePort
I found a bit of a way around this so that the context wouldn't be lost. Here's what I did to get this to work.
TestRoute changes to this:
routes.MapRoute(
"TestRoute",
"{path}/{controller}/{action}/{id}",
new { controller = "Space", action = "Index", id = UrlParameter.Optional },
new { path = #"TEST" },
new string[] { "The.Namespace" });
Setting the constraint on path and removing it from the route makes this routing work. Now I can hit the /TEST/Space/Index and all my links generated from ActionLink behave as intended. Also on a related issue, I ended up adding the namespace specification in the map route, as the development environment required that be in there to properly route things to the TEST path.
Some of the info I found was on this page.
If you do:
#Html.ActionLink("Text", "Index", "Space")
That is going to match the first route in your collection (TestRoute). This is the default behavior.
If you want to choose a specific route then use #Html.RouteLink instead.
If you want to target a specific route, you could use RouteLink extension, it allows you to specify which exact route should be used to generate the link.
#Html.RouteLink("with Test", "TestRoute")
#Html.RouteLink("with Test", "TestRoute", new {controller="Space", action="Foo"})
#Html.RouteLink("without Test", "Default", new {controller="Space", action="Foo"})
How should I configure the following non area routes?
/foo/{controller}/{action}/{id}
maps to controllers in namespace myapp.foo.
/{controller}/{action}/{id}
maps to controllers in namespace myapp.
I also have 2 areas, bar and baz, they are registered with registeraAllAreas.
My current setup
This is my current setup. It gives the problem below when I use the url /Home/Index.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("myapp/elmah.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
"foo", // Route name
"foo/{controller}/{action}/{id}", // URL with parameters
new { action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "myapp.Controllers.foo" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "myapp.Controllers" }
);
Multiple types were found that match the controller named 'Menu'. This
can happen if the route that services this request
('foo/{controller}/{action}/{id}') does not specify namespaces to
search for a controller that matches the request.
The request for 'Menu' has found the following matching controllers:
myapp.Controllers.MenuController
myapp.Areas.bar.Controllers.MenuController
myapp.Areas.baz.Controllers.MenuController
Clearly there's something I'm doing the wrong way.
Update
I also get the wrong adress generated when I use:
<% using (Ajax.BeginForm("SaveSomething", "Home", ...
It renders <form target="/foo/Home/SaveSomething"
I'm guessing that one cannot reliably use {controller} in two routes in the same area.
Update 2
It seems to work much better when I put the /foo route registration at the bottom.
This raises the question, what is considered a/the default route? (As the default route is reccomended to be put at the very end.)
You have two controllers that has the name MenuController so MVC doesn't know which one to use if you don't give it more information. In you areas you probably have a files named something like <YourAreaName>AreaRegistration. Open those files and update the RegisterArea method so you route the request to the right controller.
From your error message it seems like the route is getting mapped to foo/{controller}/{action}/{id}, which doesn't have a MenuController. My guess is that you have a action link on a page under foo something something. That will generate an incorrect link if you don't specify the area for the link.
Try this to use the default route with ActionLink:
#Html.ActionLink("Some text", "action", "controller", new { area = "" }, null)
If you want the request to go to a specific area just write it down in the call.
UPDATE: The problem is that when you write something like Ajax.BeginForm("SaveSomething", "Home",...) it will match the first route. You can't solve this by putting the area in the BeginForm statement as I suggested before since the foo route is not an area. You have two options, 1: move the foo part to an area, 2: put the foo route after the default route. If you put the default route before the foo route you will get a hard time rendering urls as long as you have foo in the same area as the default route (the default area), since the route engine will always find the default one first. However, you will be able to catch request to the foo route. So my best suggestion is to put the foo route in an area.
I have these 2 routes mapped out:
routes.MapRoute(
"Admin",
"admin/{controller}/{action}/{id}",
new { controller = "Admin", action = "index", id = "" }
);
and then I have:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
So the 2 routes are identical, except the first one has /admin prefixed in the URLS.
This is what is happening, I have no idea how to explain this:
When I go to:
www.example.com/user/verify
it redirects to
www.example.com/admin/user/complete
instead of
www.example.com/user/complete
The action Verify simply redirects to Complete like this:
return RedirectToAction("complete", "user");
And all the complete action does is populate the ViewModel, and then calls the view.
How can it be redirecting and adding the prefix /admin/ to the URL?
I believe it is redirecting to the Admin route because the Admin route is the first with all the matching parameters (controller and action in the case provided). If you want to use something like this you will need to either look into using areas (MVC2) or using a named route redirect.
admin is your controller, you dont need an admin/controller/action the default route works just fine
all you need is an admin controller and the default route will find it for you
ie {controller}/{action}/{id}
will send /admin/addproduct to a controller named admin and an action called addproduct
you only need to add routes if you want something custom for example
/products/televisions/hdtv/2
where products would be a controller and the last 3 are category,subcategory and pagenumber
on the controller you point it to within your route.
hope that makes sense
Not sure exactly how your controllers are structured, but you can add a constraint to the first MapRoute to limit it to the specific controllers you want the route to apply to:
routes.MapRoute(
"Admin",
"admin/{controller}/{action}/{id}",
new { controller = "Admin", action = "index", id = "" } ,
new { controller = "[Some regex Expression - e.g. Admin]" }
);
Which will make the route only applicable for those controllers related routes. You can also use this tool to debug your routes. Depends how you have things structured, but like #NickLarson said - sounds like your using area functionality of MVC 2.
mvc goes from top to bottom while matching router, that' why you are dealing with this problem