I am creating an application in ASP.NET Core 2.2. In the Startup.cs file there was already a default route and I defined one other route for an admin area:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "admin",
template: "{area=Admin}/{controller=User}/{action=Index}/{id?}");
});
The admin area is authorized by an Admin role using [Authorize(Roles = "Admin")]. But when I start the application, it's calling that area route by default, although the user can't see anything and he gets an unauthorized page. Why is the application using the area route as the default route?
By saying {area=Admin} you are making the area part optional. So a route not containing an area will also be matched by that (with Admin as the chosen area).
If you want to make sure that only a path /Admin/ triggers your area, you could do it like this:
routes.MapRoute(
name: "admin",
template: "Admin/{controller=User}/{action=Index}/{id?}",
defaults: new { area = "Admin" });
You can also use this shortcut method which also sets up a route constraint for your area:
routes.MapAreaRoute("admin", "Admin",
"Admin/{controller=User}/{action=Index}/{id?}");
Also, the order in which you register your routes is also important. In general, the first route template that matches a route will be used. So since your admin route is rather specific, you should probably list that first, and only then fall back to a default route.
As the documentation on routing areas in MVC explains:
Conventional routing is order-dependent. In general, routes with areas should be placed earlier in the route table as they’re more specific than routes without an area.
Related
I'm using RedirectToAction to redirect to a different view, however I'm using an application in a site in IIS so there is a prefix that has to go to the path.
When I do RedirectToAction it just redirects on the root.
I need it to respect the current path and redirect along those lines.
For example:
I'm on https://localhost:8085/app/custompath/controller1/action
and I call
return RedirectToAction("controller2", "action");
and I get redirected to
https://localhost:8085/controller2/action
instead of
https://localhost:8085/app/custompath/controller2/action
RedirectToAction, or any of the similar methods that let you generate a URL based on route information like controller, action, name, etc. only work for the currently running application in context. You cannot use them to generate URLs for routes in other applications.
You can use simply Redirect with a string URL, but it's on you to generate the URL you need. The typical approach is set the base path for your external application in an application setting.
If you have something like this in your Starup.cs:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "custom",
template: "app/custompath/{controller=Home}/{action=Index}");
});
Then you can do this to redirect:
var url = Url.RouteUrl("custom", new { controller = "controller2", action = "action" });
return Redirect(url);
By the way .NET Core has built-in URL Rewrite if you need.
I have a typical ASP.NET MVC controller, but I just want to change its route. The default route now is:
Blog/{controller}/{action}/{id}
I want to change the route of a specific controller to
Blog/Admin/{controller}/{action}/{id}"
I tried to achieve this by adding the Route, RouteArea and RoutePrefix attributes to the controller but without any success.
How can I achieve this?
Add this route prior to the default
routes.MapRoute(
name: "BlogAdmin",
url: "Blog/Admin/{action}/{id}",
defaults: new { controller = "YourSpecificControllerName", action = "Index or other default action name", id= UrlParameter.Optional });
Since this is for a specific you don't need {controller} part in your url. If you still want to specify it change the url argument to "Blog/Admin/YourSpecificControllerName/{action}/{id}" where YourSpecificControllerName is the name of your controller.
Also since the order of rote registration matters make sure that this route registered prior to the the default one
I've got controller named IHateYou, and got loads of views inside that I can access through typing ...\IHateYou\User1. I need to change the name of controller to Users in url, but the rest stays the same, so the users can still enter it through ...\Users\User1. I've tried adding route, but I could still access it by the prior way.
i think you should add a constraints to your default route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "IHateYou", action = "Index", id =UrlParameter.Optional },
constraints: new{ controller= "[^IHateYou]" });
default route alway match controller/action/id
I have been trying to give options to users like Facebook to add their company name in the URL:
http://localhost:50753/MyCompany/Login
I have tried different URLs, but it didn't work.
routes.MapRoute(
name: "Default",
url: "{companyName}/{controller}/{action}",
defaults: new { controller = "Login", action = "Index"}
);
routes.MapRoute(
name: "Login",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Index", id = UrlParameter.Optional }
);
Now when I add this route to get it work, all of my AJAX requests start fails and those that succeed represent HTML rather than JSON. What I have noticed is that because of this route, my page gets reload again.
Can someone help me figure out how can it be done using MVC routing (if it's possible, or if I'm thinking in the wrong way)?
The problem you are having is due to the fact that both of these routes will match all URLs that have 1, 2, or 3 segments defined (because the controller and action have default values). Since routes are executed in order from the top route to the bottom route, your top route will always match and your bottom route never will match (except for the home page).
Since the top route always matches, URLs that assume that the first segment is the controller and the second segment is the action will fail because you are putting these values into the companyName and controller route keys, respectively.
For this to work as you expect, you need to make a route constraint that is aware of all of the company names.
routes.MapRoute(
name: "Default",
url: "{companyName}/{controller}/{action}",
defaults: new { controller = "Login", action = "Index"},
constraints: new { companyName = "Company1|Comany2|Company3" }
);
Note that you could implement IRouteConstraint so you could pull the values to match from a cached database model instead of hard-coding them into the configuration. See this post to learn how to create a custom route constraint.
Or, as Andy mentioned, you can make the match unique by specifying 1 or more segments of the URL explicitly.
url: "{companyName}/Login"
The idea is there must be some way to make the first route you defined not match in certain cases.
Alternatively, you could implement RouteBase, but you would only need to if you require much more control over the matching process than this simple scenario.
The problem is there is no way to distinguish between your two routes. For example /a/b/c could be the Default route with company = a, controller = b, action = c or it could be the Login route with controller = a, action = b, id = c.
To solve this you'll need to design your routes, including the ones for AJAX, so that there is no way two routes could have the same URL. In your example you could just drop the /{id} from the login route as it isn't needed. Also specify the URL more specifically and put it before the default. This would give you something like
routes.MapRoute(
name: "Login",
url: "{companyName}/Login",
defaults: new { controller = "Login", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{companyName}/{controller}/{action}",
defaults: new { controller = "Login", action = "Index"}
);
In this case both /MyCompany/Login and /MyCompany/Login/Index would go to the login page. However MyCompany/Home/Index would go to controller = Home, action = Index.
Personally, I tend to remove the default route altogether so I can specify the URLs I want rather than have them all as /controller/action. That gives you more control but does mean specifying each route individually.
So I am creating a site with two bindings
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
name: "DefaultAdmin",
url: "Admin/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
The idea is I want to have EndUsers to access the normal pages (/account/login etc etc ) but have a admin only portion of the site (with a different layout) for admin users.
The question is kinda two fold :-
In terms of controllers it looks like MVC just looks in the Controllers folder, is there a way to seperate out AdminControllers and Regular controllers to keep things organised?
I would like to have a separate "master view" appear for the admin than the regular, currently I'm just using _layout.cshtml from _start.cshtml but I'd like to be able to use _layout.cshtml and _adminLayout.cshtml without prefixing every view with the name of the view (if not then I can live with this one easily enough).
Any help would be apprechited.
You can place controllers anywhere in the assembly as long as they are derived from System.Web.Mvc.Controller class they will be identified by the routing logic.
You could look at "Areas" in MVC to help out with your specific need.
It sounds like Areas in MVC is what you are looking for.