Here is necessary code to reproduce a very strange problem with ASP.NET MVC 3.0 routing:
Route registration in Global.asax.cs:
routes.MapRoute("History", "Customer/History", new {controller = "User", action = "History", someParam = UrlParameter.Optional});
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Here we declare a route to the user's history. But in the URL we want "Customer" instead of "User". Also please note parameter someParam. Controller User does really exist and has action History.
Now usage in view:
History
History with param
I am using here Url.Action() instead of Html.ActionLink() only for clarity.
And here is the result - how this part of the view was rendered:
History
History with param
Now the problem is clear - URL without parameters was resolved correctly, while the URL with parameter starts with "/User" instead of "/Customer".
Questions:
Is it a normal behavior? If yes, why does routing work that way?
Is there any workaround for this? I mean is there any way to get the final result as:
History
History with param
I suspect it's getting confused because your route for Customer doesn't list that extra value, but the default one does. Try this:
routes.MapRoute("History", "Customer/History/{someParam}", new {controller = "User", action = "History", someParam = UrlParameter.Optional});
Or to preserive the query string link syntax, this:
routes.MapRoute("History", "Customer/History/{id}", new {controller = "User", action = "History", id = UrlParameter.Optional});
In the second case you don't supply a value for id when creating the link (your call to Url.Action shouldn't have to change).
Related
I'm trying to create a new Route in MVC3 to achieve the link http://localhost/Product/1/abcxyz:
routes.MapRoute(
"ProductIndex", // Route name
"{controller}/{id}/{name}", // URL with parameters
new { controller = "Product", action = "Index", id = UrlParameter.Optional, name = UrlParameter.Optional } // Parameter defaults
);
And I used Route Link like this:
<li>#Html.RouteLink("My Link", "ProductIndex", new { controller = "Product", id = 10, name = "abcxyz" })</li>
Product Index action:
public ViewResult Index(int id, string name)
{
var product = db.Product.Include(t => t.SubCategory).Where(s => s.SubID == id);
return View(product.ToList());
}
The url render as I expected. But when I click on it, I got a 404 error with message
HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly
UPDATE
I place that Route above Default Route and the URL work fine. But there's a problem occure. My index page http://locahost point directly to Index action of Product controller, but I want it points to Index action of Home controller instead
It's because you have 2 optional parameters in your route and the engine can't work out which one to set the value to. See my answer to a similar issue here
You can create a specific route for your products controller first (with mandatory id) and then have the generic fallback route afterwards.
routes.MapRoute(
"ProductIndex", // Route name
"products/{id}/{name}", // URL with parameters
new { controller = "Product", action = "Index", name = UrlParameter.Optional } // Parameter defaults
);
Try it
routes.MapRoute(
"Default", // Route name
"{controller}/{id}/{name}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, name = UrlParameter.Optional } // Parameter defaults
);
for routing details see this link. In this link every type of routing is discussed.
http://www.codeproject.com/Articles/408227/Routing-in-MVC3
I have a MVC3 app with several routes. Two of them are defined like this:
routes.MapRoute(
null,
"System/{name}", // URL with parameters
new { controller = "Systems", action = "Index" } // Parameter defaults
);
routes.MapRoute(
null,
"Carrier/{name}", // URL with parameters
new { controller = "Carriers", action = "Index" } // Parameter defaults
);
Now, in my menu I have two links to these routes that are created using Url.Action:
Url.Action("Index","Systems")
Url.Action("Index","Carriers")
Now when i start the application, everything seems fine, and the links in the menu show as /System/ and /Carrier/, and that is the expected value.
However, when i browse to for example /System/MySystem in the web page i would still want the links to point to the same place, but now they point to /System/MySystem and /Carrier/MySystem.
I have tried lots of things to keep the link from using the name from the route value, but to no avail. The strangest case i experienced was when i tried this:
Url.Action("Index","Systems", new{name = (string)null})
Now the link showed up as
/System?name=MySystem
Are there any good way here to make sure that the name value from the route values does not interfer with these links in any way?
As you noticed the Url. helpers reuse the previously given route parameters.
As a workaround (I hope that there is a more elegant solution...) you can remove the name entry from the RouteData.Values in your view:
So before calling you Url.Action in your view:
Url.Action("Index","Systems")
Url.Action("Index","Carriers")
Remove the prefilled name from the RequestContext:
#{
Request.RequestContext.RouteData.Values.Remove("name");
}
It's also an workaround but if you slightly modify your routes with providing a default null value for your name segment:
routes.MapRoute(
null,
"System/{name}", // URL with parameters
new { controller = "Systems", action = "Index", name = (string)null }
);
routes.MapRoute(
null,
"Carrier/{name}", // URL with parameters
new { controller = "Carriers", action = "Index", name = (string)null }
);
Your original solution ("nulling" the name in the Url.Action) will also work :
#Url.Action("Index", "Systems" , new {name = (string)null} )
I would like to create a member area on my site with the following URL patterns:
Pattern for logged out user:
domain.com
domain.com/About
domain.com/Blog
domain.com/Blog/1 (where 1 is the post ID)
But I also have a member area where I prefix the Url with Member like this:
domain.com/Member/MyProfile
domain.com/Member/MySettings
This seems simple, but I can't see an obvious way to make routing rules for this. I have:
routes.MapRoute(
"Member", // Route name
"Member/{controller}/{action}/{id}", // URL with parameters
new { controller = "Task", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
This works great for the member when you are logged in, but unfortunately the first rule also matches the logged out view and Url.Action("Blog", "Home") produces a Url that looks like this:
domain.com/Member/Home/Blog
How do I tell Url.Action that it must form Urls with the default rule outside the member area?
You could use a real MVC area instead of trying to simulate one. There's also a video you might checkout. The idea is that you leave your default route definition in Global.asax and then add a Member area to your site which will have a separate route configuration.
Suppose, I have a controller named category with an action method, Index which takes id as parameter.
Therefore, the URL appears like this : category/Index/foo. As you can see, the Index segment just doesn't seem right. A URL such as this : category/foo will be more readable and understandable.
Just like in SO, these guys use : question/857344
How can I achieve such a URL. In my routes, I have set defaults for all three : controller, action and id. But, when i try to visit category/foo, I get the "404 - resource not find"
routes.MapRoute(
"Category/{id}",
new { controller = "Category", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
You map it how you want it to look then specify the Action.
Also make sure you put it before the default route
I am trying to create a route that can allow for different formats (html/json/xml etc)
This is what I am trying, but it doesn't work.
routes.MapRoute(
"Default",
"{controller}/{action}/{id}/{format}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional, format = "html" },
new { format = #"html|json" , id=#"\d+"}
);
The routes that do work are these:
/Person/details/1
/Person/details/1/json
But this don't work:
/Person which imo should default to /Person/Index/html
/Person/json and imo should lead to /Person/Index/json
But it doesn't match.
For the second of the ones that don't work I assume it thinks json is an action and that's the problem there, but for the first one I don't fully get it as I have defaults for each part of the url, and id is optional and it can't think html/json is the id as I say id have to be a number anyway, so it should imo get that one.
So who aren't the first one working?
For the second one I have been meaning to write a regex like this (I know it's not a real regex btw, any help on that is also appreciated..): action = #"!(html|json|\d+)" so that it will see that I'm not trying to say that json/html is an action, but that it then should use the default action of index.
But since the first one isn't even working I think I have to resolve that one first.
The problem
Routes can have multiple optional parameters (although I suggest you don't use this unless you know Asp.net MVC routing very well), but you can't have non-optional parameters after optional ones as you've done it...
Imagine what would happen if you set a non-default "json" value for your format but don't provide id? What would come in place of the id? You'd run against a very similar problem with multiple optionals, hence I advise you not to use them.
Two solutions
Change parameter order:
"{controller}/{action}/{format}/{id}"
Use two routes
routes.MapRoute(
"Ordering",
"{controller}/{action}/{format}",
new { controller = "Home", action = "Index", format = "html" },
new { format = #"html|json|xml"}
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}/{format}",
new { controller = "Home", action = "Index", format = "html" },
new { format = #"html|json|xml", id = #"\d+"}
);
The first one will cover requests where ID is optional and you do provide format, and the second one will cover situations when ID is present.
The id parameter cannot be optional. Only the last parameter of a route definition can be optional.