In ASP.NET MVC, is it possible to define routes that can determine which controller to use based on the data type of part of the URL?
For example:
routes.MapRoute("Integer", "{myInteger}", new { controller = "Integer", action = "ProcessInteger", myInteger = "" });
routes.MapRoute("String", "{myString}", new { controller = "String", action = "ProcessString", myString = "" });
Essentially, I want the following URLs to be handled by different controllers even though they have the same number of parts:
mydomain/123
mydomain/ABC
P.S. The above code doesn't work but it's indicative of what I want to acheive.
Yes, if you use constraints:
like so:
routes.MapRoute(
"Integers",
"{myInteger}",
new { controller = "Integer", action = "ProcessInteger"},
new { myInteger = #"\d+" }
);
If you put that route above your string route (that doesn't contain the constraint for #"\d+"), then it'll filter out any routes containing integers, and anything that doesn't have integers will be passed through and your string route will pick it up.
The real trick is that Routes can filter what's coming through based on Regular Expressions, and that's how you can determine what should pick it up.
Related
I have a simple query on ASP.Net Web API Routing. I have the following controller:
public class CustomersController: ApiController
{
public List<SomeClass> Get(string searchTerm)
{
if(String.IsNullOrEmpty(searchTerm))
{
//return complete List
}
else
{
//return list.where (Customer name contains searchTerm)
}
}
}
My routing configuration (Conventional) looks like this:
config.Routes.MapHttpRoute(name:"DefaultApi",
routeTemplate:"api/{controller}/{id}"
defaults:new {id = RouteParameter.Optional});
config.Routes.MapHttpRoute(name:"CustomersApi",
routeTemplate:"api/{controller}/{searchTerm}"
defaults:new {searchTerm = RouteParameter.Optional});
If I hit the url:
http://localhost:57169/api/Customers/Vi
I get a 404-Not found
If I reverse the order of the routes, it works.
So the question is in the first case, is it matching the first route (DefaultApi)? If not, why is it not trying the second route?
This route template
config.Routes.MapHttpRoute(name:"DefaultApi",
routeTemplate:"api/{controller}/{id}",
defaults:new {id = RouteParameter.Optional}
);
match your URL because Id can be any type : string, int etc. So your URL respect this template and this route is chosen.
To make this template more restrcitive and make ASP.Net Web API to go to the next template you need to add to it some constraints by saying something like "Id parameter must be a numeric type for this template". So you add constraints parameter like below:
config.Routes.MapHttpRoute(name:"DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Required}, // <- also here I put Required to make sure that when your user doesn't give searchTerm so this template will not be chosen.
constraints: new {id = #"\d+"} // <- regular expression is used to say that id must be numeric value for this template.
);
So by using this URL http://localhost:57169/api/Customers/Vi the above template will be skipped and the next will be chosen.
Here is my route, the only required param is the controller
routes.MapRoute(
"countryoptional/controller",
"{country}/{controller}/{pagelabel}/{page}",
new { action = "index", country = UrlParameter.Optional, pagelabel = UrlParameter.Optional, page = UrlParameter.Optional },
new
{
pays = #"$|^(france|belgium)$",
controller = #"^(car|boat)$",
pagelabel = #"^$|page",
page = #"^$|\d{1,6}"
}
);
I'm expecting the following URLS to work with :
/car/
/car/page/2
/france/car
/france/car/page/2
It kind of works :
Url.Action("index", "car", new { country= "france", pagelabel = "page", page = 2} )
Will produce : /france/car/page/2
BUT
If I want an url without a country the view action respond but a constructor like
Url.Action("index", "car", new { pagelabel = "page", page = 2} )
will produces this link : //car/page/2
I get this double slash "//car" so it breaks the link of course.
I suspect the fact it does not like the possibility of the {country} parameter preceding controller optional in the {country}/{controller}/... url definition
I don't want to complicate my route config and having another route declaration
There must be a way, what I'm I doing wrong ?
Optional parameters must follow any required parameters. There's no way around this that I know of. The same limitation applies everywhere else (your method definitions, etc.).
Just repeat the route and have one with a required country and one without any country. MVC will work out which one to use.
I want a user to be able to access objects (could be JSON or XML) using a restful syntax rather than having to use query strings.
So instead of http://mywebsite.com/objects/get=obj1&get=obj2&get=someotherobject/ they could do something like http://mywebsite.com/objects/obj1/obj2/ and the xml/JSON would be returned. They could list the objects in any order just like you can with query strings.
In asp.net mvc you map a route like so:
routes.MapRoute(
"MyRoute",
"MyController/MyAction/{param}",
new { controller = "MyController", action = "MyAction", param = "" }
);
I would want to do something like:
routes.MapRoute(
"MyRoute",
"MyController/MyAction/{params}",
new { controller = "MyController", action = "MyAction", params = [] }
);
where the params array would contain each get.
Not quite.
You can create a wildcard parameter by mapping {*params}.
This will give you a single string containing all of the parameters, which you can then .Split('/').
You could use a catchall parameter
routes.MapRoute(
"MyRoute",
"MyController/MyAction/{*params}",
new { controller = "MyController", action = "MyAction"}
);
This would pass params as a string that you could split on a / to get an array.
I'm new to MVC land, and have an app that i'm working on. i have 2 different links with 2 routes in my global which are fairly similiar
route 1
routes.MapRoute("Category", "Movies/{category}/{subcategory}",
new { controller = "Catalog", action = "Index", category = "", subcategory = "" });
route 2
routes.MapRoute("Movie", "Movie/{movie}",
new { controller = "Movie", action = "Index", movie = "" });
When i call an actionlink for the first route it creates it as i would think it should:
.../Movies/Category/SubCategory
however when i create my second link it populates it like this:
.../Movie?movieId=ff569575-08ec-4049-93e2-901e7b0cb96a
I was using a string instead of a guid before and it was still doing the same i.e.
.../Movie?movieName=Snatch
my actionlinks are set up as follows
<%= Html.ActionLink(parent.Name, "Index", "Catalog",
new { category = parent.Name, subCategory = "" }, null)%>
<%= Html.ActionLink(movie.Name, "Index", "Movie",
new { movieId = movie.MovieId }, null)%>
My app still works, but i thought this behavior was strange. any help would be great.
Thanks!
routes.MapRoute("Movie", "Movie/{movieId}",
new { controller = "Movie", action = "Index", movie = "" });
Should the route text not match the name of the property which you are submitting to the mvc link?
The problem is that when you call ActionLink, the routing system can't figure out which of the two routes to use, so it chooses the first. The solution is to use RouteLink instead of ActionLink. RouteLink lets you specify the name of the route to use when generating the URI. Then there is no ambiguity as to which route to use. I think that ActionLink is obsolete. I can think of no reason to use it in lieu of of RouteLink.
However, you may still have a problem when the user submits links. In that case, use route constraints to enforce the selection of the correct route.
Andrew is correct (up-voted) that the tokens you use in ActionLink/RouteLink and the route itself must match.
On an ASP.NET MVC (Beta) site that I am developing sometimes calls to ActionLink will return to me URLs containing querying strings. I have isolated the circumstances that produce this behavior, but I still do not understand why, instead of producing a clean URL, it decides to using a query string parameter. I know that functionally they are the same, but for consistency (and appearance) of the URLs this is not what I want.
Here are my routes:
routes.MapRoute(
"Photo Gallery Shortcut",
"group/{groupname}",
new { controller = "Photos", action = "All", Id = "" });
routes.MapRoute(
"Tagged Photos", //since the Tagged action takes an extra parameter, put it first
"group/{groupname}/Photos/Tagged/{tagname}/{sortby}",
new { controller = "Photos", action = "Tagged", Id = "", SortBy = "" });
routes.MapRoute(
"Photo Gallery", //since the Gallery's defualt action is "All" not "Index" its listed seperatly
"group/{groupname}/Photos/{action}/{sortby}",
new { controller = "Photos", action = "All", Id = "", SortBy = "" });
routes.MapRoute(
"Group", //<-- "Group" Category defined above
"group/{groupname}/{controller}/{action}/{id}",
new {controller = "Photos", action = "Index", Id = ""});
Now the problem only occurs when I am looking at the view described by the route named "Tagged Photos" and execute ActionLink via:
Html.ActionLink<PhotosController>(p => p.All((string)ViewData["group"], ""), "Home")
Which produces the URL:
http://domain/group/GROUPNAME?sortBy=
From any other view the URL produced is:
http://domain/group/GROUPNAME
I have pulled down Phil's ASP.NET Routing Debugger, and everything appears in order. This one has me stumped. Any ideas?
Not sure why different views are producing different URLs.
But you can get rid of that sortBy param by assigning a default value to the first route.
new { sortBy = "" }
During generation, if sortBy matches the default, the route engine will skip that parameter (if it's in the query string).
You're going to have to use named routes here, not action routes, because of the way routing works in ASP.NET, because it does "first match", not "best match".
I think it is picking up your first Route. It too has the action All. And because the sortby is not specified it is exposing it as a querystring parameter
This will still work with the action method 'All' on the PhotosController, because it just fills the sortby parameter with the query string value.
In the Route Debugger is it executing the 3rd route or the 1st?