ASP.NET MVC Routing help - asp.net-mvc-3

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.

Related

ASP.NET MVC3 custom routing

I have three routes that look like that:
all.html
all/page-{numpage}.html
all/page-{numpage}-limit-{limit}.html.
First two work ok - which means that in my controller I'm getting the value of numpage or 1 if none is given:
public ViewResult All(int numpage = 1, int limit = 10) {}
numpage is whatever I typed in the address bar
Third route doesn't work at all - as if I went to the first route (all.html), so it's value is equal to 1, and limit is 10 - the defaults. However when I go to all/page-4.html?limit=3 I get correct values. What am I doing wrong? :D
One more thing - I create my routes dynamically, so code for registering them looks like that (rcache returns correct list of routes):
List<Tuple<Dictionary<string, string>, string, string, string>> routes = rcache.GetRoutes();
foreach (var route in routes) {
foreach (KeyValuePair<string, string> kvp in route.Item1) {
context.MapRoute(
route.Item4,
kvp.Value,
new { controller = route.Item2, action = route.Item3, id = UrlParameter.Optional, name = UrlParameter.Optional, numpage = UrlParameter.Optional, limit = UrlParameter.Optional }
);
}
}
ok well you are fighting with yourself. Why don't you put the last rule as first, and see what happens? Order of route rules makes a difference.
Just a quick note. Simplify your design and avoid complexity. What you are doing might work, but it's a terrible choice. Don't do it.
first of all you shouldnt allow anyone to add/edit/delete your routes. if someone doesnt really understand how it works, you are allowing them to break your application. i would definetely avoid it.
Well lets say you have to do it. then you need to validate the routes and rules as well, you cant just push it to your route table.
Also, lets say they add a route and it is not working, because your controller doesnt support it. you will take the blame. let them come to you with a request and then you add it to your route table.
You are the author of this code, no one else will be able to maintain what you wrote or what you were thinking while you were writing.

.NET MVC Routing works, except for one piece

Little help here and advice.
Working on my first MVC application and I've got an entity of Students setup.
Student Controller and views with basic CRUD capabilities.
mysite.com/Student gets me there.
Now I want to add Payments, so I've added a Payments controller and views with basic crud.
that gives me mysite.com/Payments
I want payments to go a URL that looks like: mysite.com/Student/Payments
So I researched URL routing and (I think) I had it backwards for a long time as nothing seemed to work. But now, I've created this additional Route:
routes.MapRoute(
"Payments",
"Student/Payments/{action}/{id}",
new { Controller = "Payments", action = "Index", id = UrlParameter.Optional }
);
And now it all seems to work properly. When I send an ActionLink to any action in the Payment controller, the URL is correct. For example: www.mysite.com/Student/Payments/Edit/5 comes up as the URL.
The problem I'm having is that Payments is still a base URL route. So I can also get to payments by going to www.mysite.com/Payments
How do I "remove" that route, so that mysite.com/Payments is not valid? Or am I doing this all ass-backwards in some way?
Any help appreciated.
Your kind of thinking about it the wring way around. The mapping configuration just supplies a hierachical list of rule to specify where a particular url's code lives.
So when you say it's still hitting mysite.com/Payments. That's because it's hitting the default rule in your Global.asax
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional} // Parameter defaults
);
You could remove this but then no default rules will work.
or you can add an ignore rule. In your case something like
routes.IgnoreRoute("Payments/{action}/{id}");
make sure you put this above the default rule.
You need to use overload of MapRoute method for your default route i.e.:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index" },
new { controller = ""}); //there constraints for controller goes
Look at this blog post about creating custom constraints, there is example for "not equals"

MVC3 Routing Basics

I am learning MVC routing. Hope my question doesn't look silly, and please help :)
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Msdn reference says it takes a String, String, Object, so I try to make a small change (added a "my" in front of everything just to mod the names and see if it works):
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{myresource}.axd/{*mypathInfo}");
routes.MapRoute(
"myDefault", // Route name
"{mycontroller}/{myaction}/{myid}", // URL with parameters
new { mycontroller = "Home", myaction = "Index", myid = UrlParameter.Optional } // Parameter defaults
);
It doesn't work any more. What is the format of these strings in "{}" curly brackets, and the anonymous object value formats.
{controller}/{action}/{id}
/Products/show/beverages
{table}/Details.aspx
/Products/Details.aspx
blog/{action}/{entry}
/blog/show/123
{reporttype}/{year}/{month}/{day}
/sales/2008/1/5
{locale}/{action}
/US/show
{language}-{country}/{action}
/en-US/show
{controller}/{action}/{id}
http://server/application/Products/show/beverages
{resource}.axd/{*pathInfo}
http://server/application/WebResource.axd?d=...
I google'd around, but all the posts seem to assume I know the formats, and couldn't find any detail explanation.Do they have to be fixed names like {controller} {action} {id} etc, or they won't work? Does the default anonymous object value names need to match them, too? Further, what does the "*" mean in {*pathInfo} I couldn't find the explanation for that, neihter. Thank you.
First, we need some definitions here.
Let's break down the default route.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
In this case, Default, on line 2, is merely a text name used to identify the route.
On line 3 is the url pattern. This defines how the route is matched. the things in braces are placeholders. They map to parameter names. so {controller} maps to the controller name. {action} maps to action name, and {id} maps to parameter called id.
On line 4 is the defaults object. This object supplies default values when they cannot be inferred from the url.
So, if we take all that together, here are some conclusions we can draw:
the default objects only supply values when they cannot be inferred from the url string. Thus, when the incoming request is just /, the dfault values from line 4 are used for controller and action. If the incoming request is /Blah, then the default controller supplied on line 4 is ignored, and instead MVC looks for BlahController. However, since no action was specified, the default action from line 4 is used Index.
The key thing to remember here is that the values on line 4 are only used if nothing matches the url in line 3.
So, when you changed everything, you threw everything for a loop. It's a meaningless route, because nothing defines which controller or action to use, and those two values are required in order to complete the route. As such, MVC can't figure out what controller you want. Or action method for that matter.
Another example:
routes.MapRoute(
"Example",
"Home/{action}/{myid}",
new { controller = "NotHome", action = "Index", myid = UrlParameter.Optional }
);
Because there is no {controller} placeholder in the url, then the default of "NotHome" is used, which makes MVC look for NotHomeController. So a url of /Home/About/3 would mean that controller = "NotHome", action = "About", and myid = 3.
All in all, in an incoming route, something must fill in the value for controller and action at a minimum. id is optional, and can be renamed to whatever you like. But, something must set controller and action parameters or MVC doesn't know how to route things.
Also, remember, the default route (or an effective default route) must come last in the list, otherwise no other routes will be matched.
The {*pathInfo} bit is called a slug. it's basically a wildcard saying "everything after this point is stuffed into a parameter called pathInfo". Thus if you have "{resource}.axd/{*pathInfo}" and a url like this: http://blah/foo.axd/foo/bar/baz/bing then two parameters get created, one called resource, which would contain foo and one called pathInfo which contains foo/bar/baz/bing.

mvc conflicting route

I have two routes that are satisfying two different urls. how can i remove ambiguity?
Depending on which is first in the list is which is being picked up.
downloadFile and isEdit as both nullable bool.
context.MapRoute("Asset_GetImage", "Admin/{controller}/{action}/{assetId}/{downloadFile}");
context.MapRoute("News_Read", "Admin/{controller}/{action}/{newsId}/{isEdit}");
thanks
You don't need separate routes at all; you are collecting the exact same data types in each.
Just change assetId and newsId both to simply id and code your controllers accordingly. The controller name is already in the URL.
you could make the routes more specific.
For example, specify the {controller} for each of these:
context.MapRoute("Asset_GetImage", "Admin/Asset/{action}/{assetId}/{downloadFile}");
context.MapRoute("News_Read", "Admin/News/{action}/{newsId}/{isEdit}");
you just need to make sure the controller default is added.
as an example from one of my apps:
routes.MapRoute(
"Calendar",
"Account/Calendar/{Year}/{Month}",
new { controller = "Account", action = "Calendar", Year = DateTime.Now.Year, Month = DateTime.Now.Month }
);
{controller} and {action} aren't in the route but the defaults are still set so it knows what to do with it

Route with optional parameter is not resolved correctly

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).

Resources