I am not an advanced developer. I just started working with MVC. Few days back I had seen an example of ASP.NET MVC routing code where two controller or action name has been referenced.
routes.MapRoute(
name: "test",
url: "{controller}/{action}/{page}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
defaults: new { action = "Index" }
);
Just started working with ASP.NET MVC, so I am curious to know what is the objective to mention controller or action name twice in routing code?
In above example there are two defaults.... When and why is it required?
Just requesting some one to explain the same with a nice example.
Thanks in advance.
The example from the link you posted:
context.MapRoute(
"UserHome",
"User/{id}",
new { action = "Index", controller = "Home", area = "User", id = 0,
httproute = true },
new { controller = #"Home", id = #"\d+" }
);
is a bit confusing, because it is using anonymous types without named arguments. If you add named arguments to that example, it would look like this:
context.MapRoute(
name: "UserHome",
url: "User/{id}",
defaults: new { action = "Index", controller = "Home", area = "User", id = 0,
httproute = true },
constraints: new { controller = #"Home", id = #"\d+" }
);
This makes it more clear what is going on here. The example is not showing two sets of defaults, instead there are constraints that limit the range of values that will match.
It basically says:
controller = #"Home" - When generating the URL, only match this route if controller route value is Home.
id = #"\d+" - Match if the id route value contains only numerals.
Actually, both of the above constraints run for both incoming URL matching and URL generation, but controller = #"Home" will always be true when matching the incoming URL because the default value is the only thing that can set it (and the default value is also Home).
Related
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'm trying to set up some routing in one of my MVC area's.
I have a controller named AgentGroups. I am trying to achieve the following:
Remove Index from the URL
Supply a parameter to the Index action
Allow all other actions to have their name in the URL and provide an optional parameter to them
So for example I'd like the following to work
/s/agentgroups < (Index action)
/s/agentgroups/1 < (Index action)
/s/agentgroups/someotheraction
/s/agentgroups/someotheraction/1
I currently have this in my RegisterArea method:
// s/agentgroups/action
context.MapRoute(
"Suppliers_actions",
"s/{controller}/{action}/{agentgroupid}",
new { controller = "AgentGroups", agentgroupid = UrlParameter.Optional },
new { action = "^(?!Index$).*$" }
);
// s/agentgroups/
context.MapRoute(
"Suppliers_index",
"s/agentgroups/{agentgroupid}",
new { controller = "AgentGroups", action = "Index", agentgroupid = UrlParameter.Optional }
);
This works for 3 of the 4 URL examples I gave, the one that doesn't work correctly is:
/s/agentgroups/1 < (Index action)
I'm pretty sure it thinks the 1 parameter is an action name and therefore it doesn't work..? It does work however, if, I pass the parameter like a regular query string ie: ?agentgroupid=1, but I'd like to avoid this if possible.
How can I change my routes to achieve the desired behavior?
You can reorder the area routes as Suppliers_index is more specific (only intended for the index action) than Suppliers_actions. Then you will need to add a constraint for the agentgroupid parameter in Suppliers_index.
As the parameter is optional and has to match an integer, we can use the regex \d*, but for more complicated patterns you might need to create your own route constraint as in this answer.
So your area routes may look like this: (namespaces will be different or even not needed in your case):
context.MapRoute(
"Suppliers_index",
"s/agentgroups/{agentgroupid}",
defaults: new { controller = "AgentGroups", action = "Index", agentgroupid = UrlParameter.Optional },
constraints: new { agentgroupid = #"\d*" },
namespaces: new[] { "WebApplication6.Areas.AgentGroups.Controllers" }
);
context.MapRoute(
"Suppliers_actions",
"s/{controller}/{action}/{agentgroupid}",
defaults: new { controller = "AgentGroups", agentgroupid = UrlParameter.Optional },
namespaces: new[] { "WebApplication6.Areas.AgentGroups.Controllers" }
);
I have a system that generates an "address" for a page that is used as one of the segments in a route. It can contain a hierarchical path:
Solutions/Software/List
I want to map the route with something like this:
routes.MapRoute(
"Default",
"{address}/{action}",
new { controller = "Home",
action = UrlParameter.Optional
} //defaults
);
Address can contain a hierarchical path like "Solutions/Software". List is the Action name in this case. From the address segment, we can find the correct controller and action. Other examples coud be "Solutions/Software/My-CRM-System".
From what I can understand, MVC will not match any route if the {address} contains a "/". Is it possible to override the route matching, so I can get this to work?
I know I can use a greedy catch-all to achieve most of what I want, but I would like to get a version working without catch-all: http://erraticdev.blogspot.no/2011/01/custom-aspnet-mvc-route-class-with.html
Suppose, you have maximum 4 parts in your Address. You can define your route in this way:
routes.MapRoute(
name: "Test",
url: "{AddressPart1}/{AddressPart2}/{AddressPart3}/{AddressPart4}/{action}",
defaults: new { controller = "Home", action = "Index", AddressPart1 = UrlParameter.Optional, AddressPart2 = UrlParameter.Optional, AddressPart3 = UrlParameter.Optional, AddressPart4 = UrlParameter.Optional},
lookupParameters: new string[] { "AddressPart1", "AddressPart2", "AddressPart3", "AddressPart4" },
lookupService: new LookupService());
In the lookupService, you have to specify which value belong to which AddressPart.
In your code, you can access the RouteData dictionary for AddressParts* and combine them to rebuild your address field.
I have an MVC website which used to use URLs in the standard format of: Controller/Action.
Recently, I have changed it to: Site/Controller/Action.
The problem is, there are several links to my site out there which follow the old format, and I want to redirect them accordingly.
for example: mydomain.com/Home/CustomerSearch now should go to mydomain.com/Online/Home/CustomerSearch
whereas: mydomain.com/AffiliatesHome/CustomerSearch now should go to mydomain.com/Affiliate/AffiliatesHome/CustomerSearch
How can I get it to handle the redirecting by putting in the extra routing, depending on the link they came in by?
The current routing I am using is:
routes.MapRoute(
"Default", // Route name
"{site}/{controller}/{action}/{id}",
new {site="IS", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Since I do not really see an schema in your old to new URL mapping I would suggest to add routes that match the old Controller/Action Schema and map them to the new Site/Controller/Action route schema.
So you could add the following routes
routes.MapRoute(
"LegacyHome",
"Home/{action}/{id}",
new { site="Online", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"LegacyAffiliates",
"AffiliatesHome/{action}/{id}",
new { site="Affiliate", controller = "AffiliatesHome", action = "Index", id = UrlParameter.Optional }
);
From an SEO standpoint this is not ideal because you have different URLs for the same page. A permanent redirect via status code 301 and the new URL passed in the location is better suited.
You could build a redirect controller and use the legacy routes to map legacy URLs to the redirect controller somehow like this
routes.MapRoute(
"LegacyHome",
"Home/{newAction}/{id}",
new { controller = "Redirect", action = "Redirect", newSite = "Online", newController="Home", newAction = "Index", id = UrlParameter.Optional }
);
Code of the redirect controller
public class RedirectController : Controller
{
public ActionResult Redirect(string newSite, string newController, string newAction)
{
var routeValues = new RouteValueDictionary(
new
{
site = newSite,
controller = newController,
action = newAction
});
if (RouteData.Values["id"] != null)
{
routeValues.Add("id", RouteData.Values["id"]);
}
return RedirectToRoutePermanent(routeValues);
}
}
I've got two (so far) different types of routes in my ASP.NET MVC app, one is: {controller}/{action}/{id} and the other {controller}/{action}/{title}
Currently I need to define the routes like this:
routes.MapRoute (
"Default_Title_Slug", // Route name
"product/details/{title}", // URL with parameters
new { controller = "product", action = "details", title = "" } // Parameter defaults
);
routes.MapRoute (
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "site", action = "index", id = "" } // Parameter defaults
);
Notice that the first one I've had to tie down to the product controller, this seems to be the only way I can get it work...otherwise the other routes end up looking like this:
/controller/action?id=number
Now I need to add another MapRoute call targeting another controller with the {title} segment...I don't want to create a new route for each specific entry I come up with in the future...is there a generic route I can create to map the /controller/action/title that'll play nicely with the /controller/action/id route?
Thanks,
Kieron
You can do that with a route-constraint, such as regex - a very similar example is here. Something like:
routes.MapRoute (
"Default",
"{controller}/{action}/{id}",
new { controller = "site", action = "index", id = "" },
new { id = #"\d+" }
);
routes.MapRoute (
"Default_Title_Slug",
"{controller}/{action}/{title}",
new { controller = "product", action = "details", title = "" }
);