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"]);
Related
I have used the most conventional way to build routes:
routes.MapRoute(
name: "Client",
url: "{controller}/{id}",
defaults: new { controller = "Client", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"Default,
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
But I have trouble in some routes, and I came across a new way to create routes in MVC 5.
The next example:
public class ClientController : BaseController
{
[HttpGet]
[Route"({controller}/{id})"]
public ActionResult Index(string id = null)
{
}
[Route"({controller}/{action}/{id})"]
public ActionResult GetAllClients(string id = null)
{
}
}
I wonder if it works well , and what is the real difference between them. Someone can help me?
Your first example is the configuration-based routing system, where you are handed a route builder and add your routes to it. This centralizes your route configuration code.
The second example is known as attribute routing. It allows you to specify the routes by applying attributes to controllers and action method.
They both still function. It comes down to a choice as to how you'd like to organize your code. And that's opinion based, so I will not delve into that discussion. Test both of them, and pick the one that you like best.
Note, these are not the only two options for routing. For example, SharpRouting adds functions to each controller to be called that create the routes through a fluent API. There are probably other options out there, or you can create your own!
For more information about routing in ASP.NET, see Microsoft's documentation.
Full disclaimer I work with the developer that created SharpRouting and we use it in our software (it may have been originally developed for our application, I'm not sure).
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"})
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" }
);
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