I am trying to have a custom route that use route values rather than query string, not sure why they are showing up as only query strings from a url action
Here is my route setup from my AreaRegistration
context.MapRoute(
"Member_Bank_Account",
"Member/{controller}/{bank}/{account}",
new { action = "Index" },
new[] { "Application.Web.Areas.Member.Controllers" });
And when I call it from html
<a href="#Url.Action("Index",
"Bank",
new {area = "Member", bank = "MyBank", account = "Primary"})"
class="btn btn-primary btn-sm">Primary</a>
Both urls seems to work fine..
I can go to:
http://localhost/Member/Bank/MyBank/Primary
http://localhost/Member/Bank?bank=MyBank&account=Primary
But the html link always shows query string, not sure what i am doing wrong.
Any idea how to get it to show as a route instead of having query strings?
Related
I have an ASP.NET MVC app which has areas with special routes as following:
Area registration file:
context.Routes.MapHttpRoute(
"AccessControlApi_default",
"accesscontrol/api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
context.MapRoute(
"AccessControl_dashboardwidgets",
"accesscontrol/dashboardwidgets/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new[] { "AccessControl.Controllers.DashboardWidgets" }
);
context.MapRoute(
"AccessControl_default",
"accesscontrol/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new[] { "AccessControl.Controllers" }
);
First routing is for api, second is for controllers and views in a sub-folder under the area, and third is for the default controllers under area.
Everything is working well with routing, but the issue is with the use of Url.Action like this one:
<a href='#Url.Action("Index","Home",new{area="AccessControl"})'>Go to home</a>
It's always injecting dashboardwidgets keyword in the url between area and controller like this: {host}/accesscontrol/dashboardwidgets/Home/Index
How can I generate urls according to my needs, either to root of area, or to this sub folder under the area??
I think the solution would be to use named routes in constructing your links. You would need to switch the call from
#Url.Action("Index","Home",new{area="AccessControl"})
to
#Url.RouteUrl("AccessControl_dashboardwidgets", new {area = "AccessControl", controller="Home", action="Index"})
or
#Url.RouteUrl("AccessControl_default", new {area = "AccessControl", controller="Home", action="Index"})
Depending on which route you are aiming for.
Sorry about the confusion with the parameters, was editing while doing something else... multitasking on Monday morning is obviously to be avoided:)
In order to use #Url.Action extension method you need to specify httproute name for routeValues parameter like that:
#Url.Action("Index","Home", new { httproute ="AccessControlApi_default"} )
I would like to have 2 routes similar to these:
routes.MapRoute("Detail", "guide/{urlname}", new { controller = "Application", action = "Detail" });
routes.MapRoute("Search", "guide/{keyword1}/{keyword2}", new { controller = "Guide", action = "Index", keyword1 = UrlParameter.Optional, keyword2 = UrlParameter.Optional });
So one route is a detail page that looks up an object in the database based on its url name, and the other route is a search results page based on application-generated keywords, both of which share the same url root (/guide). The two actions are in different controllers. Possible urls are:
/guide/evernote --> should route to the application detail page
/guide --> should route to search results without filter
/guide/iphone --> should route to iphone apps search results
/guide/iphone/medical --> should route to medical iphone apps search results
Obviously, like this, the second route will never be matched for a url like /guide/iphone because the first route will already match the same url.
I don't want to do a redirect in the first action if the controller can't find the object in the database. So what other alternatives are there? Do I need to create a custom RouteHandler or UrlRoutingModule for this or is there a simpler way?
If {urlname} is a url like it implies, you can add a constraint to test if the url matches a regex:
http://www.asp.net/mvc/tutorials/controllers-and-routing/creating-a-route-constraint-cs
Change the order and it will work:
routes.MapRoute("Search", "guide/{keyword1}/{keyword2}", new { controller = "Guide", action = "Index" });
routes.MapRoute("Detail", "guide/{urlname}", new { controller = "Application", action = "Detail" });
I am having trouble making #Url.Action work with Area's that have a non standard route structure.
For instance if I register this route in my Dashboard area:
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}",
new { controller = "View", action = "Display", id = UrlParameter.Optional }
);
and then in my layout view I call:
#Url.Action("Select", "View", new { area = "Dashboard" })
I get a proper url: /Dashboard/View/Select
However, if I change the route to include an optional secondary id like this:
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}/{secondaryid}",
new { controller = "View", action = "Display", id = UrlParameter.Optional, secondaryid = UrlParameter.Optional }
);
Then the same call to #Url.Action(...) doesn't return any url. If I specify those optional parameters with real values like so:
#Url.Action("Select", "View", new { area = "Dashboard", id = 1, secondaryid = 2 })
I do get a god return value of: /Dashboard/View/Select/1/2
The problem is that for some of my actions in this area don't need the id or secondary id and I want the url to be generated without them. If i set them to (int?)null it still doesn't work.
Am I doing something wrong? Shouldn't Url.Action(...) return the URL without the id and secondaryid tokens if I dont specify them in the routeValues parameter?
Having multiple optional parameters does funky things to your routes. Basically, the route engine cannot (has trouble) matching one or no optional parameters where there is a group of them. For more information, check out this blog post on the same issue.
Since you don't always need id or secondary id, just make a couple of routes to handle those cases.
context.MapRoute(
"Dashboard_IdAndSecondaryId",
"Dashboard/{controller}/{action}/{id}/{secondaryid}",
new { controller = "View", action = "Display"}
);
context.MapRoute(
"Dashboard_default_WithSecondaryId",
"Dashboard/{controller}/{action}/{secondaryid}",
new { controller = "View", action = "Display"}
);
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}/",
new { controller = "View", action = "Display", id = UrlParameter.Optional}
);
Now, when you send just an Id, just a secondaryId or both, you will have routes that will match. We can remove the optional parameter declarations in the first two routes, because in order to match that route, you would have to send the required parameters. Without sending the required parameters, you would want only the last route to match.
The last route is your default route when only Id or none is sent in the action link. I believe that this order works as well, keeping in mind you want your routes to go from most specific to least specific given that they are processed in order.
Recently I've made the transition from Web Forms to MVC 3 and I've been trying to get to grips with MVC routes. I have a somewhat peculiar problem in that when I receive a request to my application (e.g. subdomain1.organisation.com or subdomain2.organisation.com) I wish the default route to be used as follows:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
However, when a request is received by my application through a particular subdomain e.g. subdomain3.organisation.com, I want the application to default to a particular controller. I've been following code at:
http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx
which should what I want. So the code in my Global.asax is as follows:
routes.Add("DomainRoute", new DomainRoute(
"subdomain3.organisation.com", // Domain with parameters
"{action}/{id}", // URL with parameters
new { controller = "Subdomain3Controller", action = "Index", id = "" }
));
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
When deployed, my application behaves correctly when requests are sent to subdomain3.organisation.com, using Subdomain3Controller. However, when visiting any other subdomain e.g. localhost/Subdomain3Controller/Index my application seems to select the incorrect route.
My form helpers appear to return an incorrect value for:
ViewContext.Controller.ValueProvider.GetValue("controller").AttemptedValue
#using(Html.BeginForm(ViewContext.Controller.ValueProvider.GetValue("action").AttemptedValue, ViewContext.Controller.ValueProvider.GetValue("controller").AttemptedValue, FormMethod.Post, new Dictionary<string, object> {{ "id", "formid" } })){
Any ideas why this might be? Any light that anyone could shed on this issue would be much appreciated.
Many thanks in advance.
Have you tried the routing debugger, which is designed to help debug these sorts of problems
Looks like it's installable via Nuget now too.
I've also noticed that if I hard-code the controller name into the BeginForm helper, the incorrect action value is still returned:
using (Html.BeginForm("Index", "Subdomain3Controller", FormMethod.Post, new Dictionary<string, object> { { "id", "formid" } }))
{
generates:
<form id="formid" method="post" action="/">
rather than:
<form id="formid" method="post" action="/Subdomain3Controller">
when viewing:
http://localhost/Subdomain3Controller/ or http://localhost/Subdomain3Controller/Index
Stumbled on a similar problem and managed to find a solution for this problem. I'm aware this might not help the original author, but it might help some other people who end up here via search.
Anyway, it seems the route name "Default" doesn't add much weight. When BeginForm is constructing its URL it takes the first route from the route collection that 'fits'. I didn't dig too deep into how this all works internally.
What I did to solve this problem was add a constraint to the subdomain route so it can only be used for the Subdomain3Controller, as such:
var domainRoute = new DomainRoute(
"subdomain3.organisation.com", // Domain with parameters
"{action}/{id}", // URL with parameters
new { controller = "Subdomain3Controller", action = "Index", id = "" }
);
domainRoute.Constraints = new RouteValueDictionary();
webserviceRoute.Constraints.Add("Controller", "Subdomain3Controller");
routes.Add("DomainRoute", domainRoute);
[edit]
After thinking about this a little bit more and realizing my situation was a bit different from the situation of the original poster. The above won't work, because the controller is not inside of the URL anymore.
A solution might be to switch the order of the routes, and add a NotEqual constraint for the subdomains to the default route. As described in http://stephenwalther.com/archive/2008/08/07/asp-net-mvc-tip-30-create-custom-route-constraints.aspx
[/edit]
All, my situation is that I have the basic route, plus some other simple routes:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = 1}
);
So the following url works: http://somesite.com/tags/index/1
However, some of my index pages take url parameters in the following fashion:
http://somesite.com/tags/index/1?when=lastmonth
How do I use Html.RouteLink to link to this?
You can't add '?' to routes in the global asax file like this:
routes.MapRoute("TagsWhen", "Tags/index/{id}?when={when}",
new {controller = "Tags", action = "Index", id = "", when = ""});
If this route worked I could link to it using:
Html.RouteLink(string.Format("{0} ", link.Rating), "LinksWhen",
new {id=link.ReferenceId, when=Model.When})
but it doesn't! So I'm not sure how I would use a Html.RouteLink to generate http://somesite.com/tags/index/1?when=lastmonth
Just found the solution myself. You can just do a regular Html.RouteLink and any object properties you don't have mapped to the url in global.asax it adds as a url parameter.
So using this route:
routes.MapRoute(
"Links",
"Links/details/{id}",
new { controller = "Links", action = "Details", id = ""} defaults
);
and this routelink:
Html.RouteLink("Link Text", "Links",
new {id=link.ReferenceId, when=Model.When })
generates the correct url:
http://localhost:2535/Links/details/1?when=onemonth
Matthew's approach probably didn't work because he needed another null parameter on the end, otherwise it passes the route values as html attributes. :)
<%= Html.ActionLink("Link text", "Index", "Home", new { id = 1, when = "lastmonth" }, null) %>
That last MapRoute() you came up with should work fine against it.
I don't have a computer able to test this, but off the top of my head
<%= Html.ActionLink("Link text", "Index", "Home", new { id = 1, when = "lastmonth" } %>
You don't need to specify optional parameters in your routes in the global.asax.cs file. Anything that doesn't match gets chucked into the query string by default.