Okay, I've got this case where I have two controllers:
HomeController
MathController
I want the routing for my HomeController to stay as default:{controller}/{action}/{id}. But I want to access the actions in the MathController with http://myurl/Task/Math/{action}.
So what I've done is to write my RouteConfig like this:
routes.MapRoute(
name: "Math",
url: "Task/{controller}/{action}",
defaults: new { controller = "Math", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Hem", id = UrlParameter.Optional }
);
When using the above configuration and manually entering the URLs in the browser both routing methods are working. Though when trying to add a "actionLink" it always uses the Task/{Controller}/{Action} route. Even if I'm creating a link for the Home controller like this: #Html.ActionLink("Hem", "Hem", "Home", null, new { #class = "navbar-brand" })
How do I configure either my routing or my action links so that I'll get the preferred functionality?
Routes match from top down in RouteConfig.cs. Your problem is that both route configs are "catch all" routes, which means both work for any controller/action. When you use #Html.ActionLink, MVC will render the url based on the 1st route it finds, which matches your "Task" path. There are few ways to change this to get what you want.
If you want to only use the "Task" path for the Math controller, then I'd change your route to this:
routes.MapRoute(
name: "Math",
url: "Task/Math/{action}",
defaults: new { controller = "Math", action = "Index" }
);
If you want to use several controllers for the "Task" path, then you can add a route constraint. You can use it like below and specify a list of controllers (regex), or you can create your own custom Route Constraint class and implement whatever functionality you want.
routes.MapRoute(
name: "Math",
url: "Task/{controller}/{action}",
defaults: new { controller = "Math", action = "Index" },
constraints: new { controller = "Math|OtherController" }
);
Or if you want to keep all controllers/actions matching both urls, then you have to flip your routes to get the default route to display first, or you can use #Html.RouteLink like so:
#Html.RouteLink("Hem", "Default", new { controller = "Home", action = "Hem" }, new { #class = "navbar-brand" })
Related
I was just wondering if anybody can point me to right direction. Here's my problem:
I have a controller in Controllers folder called Events, and there I have some ActonResult methods Index, Search, Details, which returns Views from the
View/Events/Index,Search,Details
and some partial views from shared folder.
Now what will be the best way to display those Views without controllers name. My index view is now displayed as
localhosts|domain.com.../Events/Index or localhosts|domain.com.../Events/Details/1
and I wan't to be displayed as
localhosts|domain.com... or localhosts|domain.com.../Details/1
Now, I am curious if those changes will have any effects for my AJAX calls which are:
just part of a js code
#Url.Action("index", "Events"),
and
url: "#Url.Content("~/Events/MyAjaxSearch")",
data: "searchString=test&Location=test",
I also have some params in my Index method for pageNum(this is for infinite pagination).
Bellow is my RouteConfig code:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Events", "", new { controller = "Events", action = "Index" });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Events", action = "Index", id = UrlParameter.Optional });
Regards!
Try this:
routes.MapRoute(
name: "EventDetails",
url: "Details/{id}",
defaults: new { controller = "Events", action = "Details", id = UrlParameter.Optional }
);
I am using MVC 4 and need to remove /Home/ folder from address bar...
Eg:
http://localhost:61700/Home/AboutUs
Need to be changed as...
http://localhost:61700/AboutUs
I did that by changing the default controller in "RouteConfig.cs"
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
//url: "{controller}/{action}/{id}",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
The above code is working as expected. I do have another folders as
brand, admin etc... here I want to show the url as
http://localhost:61700/brand/productInfo ... But I am getting server
error here as Server Error in '/' Application.
Can somebody suggest me, where am I doing wrong?
Screenshots here for more info:
This is your current RouteConfig.cs configuration:
routes.MapRoute(
name: "Default",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You're telling Asp.net, when a request arrives, assume the first parameter as the action and the second parameter as the id. Right now you're not telling Asp.net to parse any parameter as the controller. Because of this it uses the default value (given as the third parameter of the MapRoute method) which is in this case Home.
In that case when parsing the request http://localhost:61700/AboutUs the values end up being:
controller: Home (it uses the default controller)
action: AboutUs (from the first parameter)
id: null (this doesn't matter right now)
When parsing the request http://localhost:61700/brand/productInfo the values end up being:
controller: Home (it uses the default controller because you haven't specified where to get the controller name from)
action: Brand (from the first parameter)
id: "productInfo"
The error you're getting is because there isn't a Brand action method in HomeController.cs with a parameter of type string named id.
Asp.net processes incoming requests by trying to match with the routes configured and it uses the first route that matches.
There are several ways to achieve what you want, which include but are not limited to:
Manually mapping every action in your HomeController.cs (choosing this method will depend on the amount of actions in your HomeController). This would look like:
routes.MapRoute(
name: "AboutUs",
url: "AboutUs",
defaults: new { controller = "Home", action = "AboutUs" }
);
routes.MapRoute(
name: "ContactUs",
url: "ContactUs",
defaults: new {controller = "Home", action = "ContactUs" }
);
// etc...
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Note how the default route is the last one, this is important because it is less specific than the others and if put before would match the request and want to look for an AboutUsController.
You could use route constraints. This would look like:
route.MapRoute(
name: "HomeControllerRoutes",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { action = "AboutUs|ContactUs|etc..." } //Here you would put all your action methods from home controller that you want to accces as /{action}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you want to read more about route constraints, I found this article that explains that the constrains parameter can receive a regular expression (I suggest you modify the regular expression above to make it case insensitive) or an IRouteConstraint.
Update:
I just read your comment about having 160+ actions in your HomeController that would make your regular expression in my second suggestion quite long. In that case the other options you have could be:
Using a regular expression that rejects all other controller names, but that would violate the open/closed principle (OCP) and every time you add another controller you would have to add it to the regular expression.
Create the regular expression from the metadata of you HomeController class. This would look like
string.Join("|", typeof(HomeController).GetMethods().Select(info => info.Name))
Or you could take a look at IRouteConstraint to see if you could figure out a more elegant solution.
I have no experience with IRouteConstraint
Add this in your route.config / glibal.asax and don't change your default routes. Add following above it.
routes.MapRoute(
name: "About",
url: "AboutUs",
defaults: new { controller = "Home", action = "AboutUs" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I have 160+ views in the home controller
You don't mention how many views you have in the other controllers, nor how complicated they need to be.
Rather than keep the default controller/action and add routes for every view in home, you can add a route for each controller and then have your default route without a controller path.
While this means you do need a route for every controller, it's better than one for every view.
routes.MapRoute(
"AdminRoute",
"Admin/{action}/{id}",
new { controller = "Admin", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"BrandRoute",
"Brand/{action}/{id}",
new { controller = "Brand", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"HomeRoute",
"{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"DefaultRoute",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
(afaicr you don't need the default route as all your views would be covered by the other 3 routes)
Note the path for 'HomeRoute' doesn't have a controller part.
As long as they are in this order any url with /Admin/ or /Brand/ will be picked up first.
I'm trying to simplify the URLs in an application, and am struggling with the route registration. This is an administrative tool. In many cases we have a List view (List.cshtml) and a Details view (Index.cshtml). The pattern that I would like to implement for these URLs are as follows:
http://mysite/person/list (This view shows a list of people)
http://mysite/person/123 (View will show details for a person with an ID of 123)
Formatting the URls that way is more of a nice-to-have feature for polishing the site. I tried several routes, and in RouteConfig here are some of the more recent routes that I've tried.
routes.MapRoute(
name: "Person",
url: "Person/{action}/{id}",
defaults: new { controller = "Person", action = "Index" }
);
routes.MapRoute(
name: "PersonID",
url: "Person/{id}",
defaults: new { controller = "Person", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional }
);
Now if I remove those custom routes, and just run with the default route, the "mysite/person/list" view works just fine. But when I select a person, the URL ends up being "mysite/person/index/[id]" instead of "mysite/person/[id]". And if I manually remove "index" from that URL and make the path "mysite/person/[id]", a "resource cannot be found" message appears.
In that second route shown, I figured that giving the route a default action would route to the Index page and treat the ID in the URL as an ID, rather than as an action. With the current configuration shown above, if I navigate to the Person/List view I'm routed to the Person/Index view.
There are a few other actions associated with that controller (like JsonResults), which I'll need to handle as well.
What is the correct way to write the routes to support the URLs that I've indicated above? Also, can you recommend a resource that shows multiple examples of route-to-URL comparisons? Thanks for your help!
=== Edit 4/9/2015 at 10:21 AM ===
Based on the answer provided by #stephen-reindl, this is the solution that I implemented. Since we have multiple interfaces with a "Detail" view, I chose a default action of "Detail". This route recognizes a GUID.
// Support URL format of http://mysite/{controller}/{guid}
// Example: http://mysite/person/474f4357-39b2-45a2-b02b-6be04b2302fe
routes.MapRoute(
name: "DetailViewWithGuid",
url: "{controller}/{id}",
constraints: new { id = #"\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b" },
defaults: new { action = "Detail", id = UrlParameter.Optional }
);
You can add a constraint that a specific route is only taken into consideration if the constraint is fulfilled:
routes.MapRoute(
name: "PersonID",
url: "Person/{id}",
constraints: new { id = #"\d+" },
defaults: new { controller = "Person", action = "Index", id = UrlParameter.Optional }
);
In this case this route is only taken if id is a number.
I have an action in controller like:
public ActionResult Index(string ssn)
{
}
and default route values: {controller}/{action}/{id}
I don't want use url like /Home/Index?ssn=1234. I want use like /Home/Index/1234.
But I also don't want to add new route values for ssn parameter (or custom model binder).
Is there some complete attribute, like [ActionName] but for parameters?
Something like this:
public ActionResult Index([ParameterBinding("id")] string ssn)
{
}
As Darin & Rumi mentioned - there are no built-in attributes, however you can achieve the same affect (across multiple controllers/actions) with a single new Route using the RouteCollection.MapRoute constraints parameter on a single route.
The following route config will apply the "SSN" route to the Foo or Bar controller, any other controller will go through the Default route.
routes.MapRoute(
name: "SSN",
url: "{controller}/{action}/{ssn}",
defaults: new { controller = "Foo", action = "Index" },
constraints: new { controller = "(Foo|Bar)", action = "Index" }
);
// default route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Edit: Alternatively you could use the ActionParameterAlias library which seems to support what you initially requested.
How I can to avoid route config like that?
I have an admin panel where user with administrator role is able to do some action(edit/delete/view) with different entities(users/profiles/addresses/...).
And i want put all this action into 1 controller.
routes.MapRoute(
name: "EditProfile",
url: "Account/Profiles/{id}/Edit",
defaults: new { controller = "Account", action = "EditProfile" }
);
routes.MapRoute(
name: "RemoveProfile",
url: "Account/Profiles/{id}/Remove",
defaults: new { controller = "Account", action = "RemoveProfile" }
);
routes.MapRoute(
name: "EditAddress",
url: "Account/Addresses/{id}/Edit",
defaults: new { controller = "Account", action = "EditAddress" }
);
routes.MapRoute(
name: "RemoveAddress",
url: "Account/Addresses/{id}/Remove",
defaults: new { controller = "Account", action = "RemoveAddress" }
);
//...
Basically I want replace all MapRoute to smth like this:
routes.MapRoute(
name: "AccountProfileActions",
url: "Account/{entities}/{id}/{subAction}",
defaults: new { controller = "Account", action = {subAction} + {entities}}
);
How can I do this?
Just don't specify the action (or controller for that matter) and rely on MVC Route conventions, i.e. the controller name and action name will be part of the URL.
If you want to have a specific route that does not match up with your controller/action names, then consider using something like AttributeRouting, which will let you specify the route right on your controller/action, instead of having to go into RouteConfig each time.