Why the query strings in my ASP.NET MVC route? - c#

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?

Related

ASP.NET MVC routing without unnecessary text

I have a problem with my MVC. Think like a URL.
domain.com/product/any-product/1
domain.com/category/any-category/1
Can I change url like this?:
domain.com/any-product/1
domain.com/any-category/1
Also can I delete id?
context.MapRoute(
name: "category",
url: "category/{category_name}/{category_id}",
defaults: new
{
controller = "Cateogory",
action = "Index",
Area = "Shop"
}
);
context.MapRoute(
name: "product_detail",
url: "product/{category}/{product_name}/{product_id}",
defaults: new
{
controller = "Product",
action = "ProductDetail",
Area = "Shop",
product_name = UrlParameter.Optional,
product_id = UrlParameter.Optional
}
);
No, you can't do it exactly like that, but you can get closer. Think for a second about what you're saying you want:
/any-product/ => ProductController
/any-category/ => CategoryController
Now, logically speaking, how is MVC supposed to sort out which URL is referring to a product and which is referring to a category?
You could do something like:
/any-category/ => CategoryController
/any-category/any-product/ = > ProductController
Now, there's some way to for MVC to work out that the URLs are different. If there's something after the category part of the path, then it's a product.
As for removing the id portion, that depends. The point of the id in the first place is that you need some sort of unique identifier for a record that should be loaded. If "any-product" is a string that can uniquely identify the record (you have column in the database with that value and there's only one with that value), then you can use that string to look up the entity instead. Then, no, you no longer need the id portion of the URL. However, if the string is not unique or doesn't correspond to data in the database, then you need the id to locate the record, and it cannot be removed.

ASP.net MVC 4 Routing and optional parameters unexpected result

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.

ASP.NET MVC URL Routes

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.

MVC ActionLink generating different types of links... why?

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.

Html.Routelink output - ASP.NET C# MVC

I have a call that looks like this:
<%= Html.RouteLink(item.displayName, "DisplayCategory", new { category = Html.Encode(item.displayName), subcat = item.searchName }) %>
and I'm getting html that looks like this:
http://localhost:1485/Home/ShowCategory/Electronics%20&%20Photography?subcat=electronics-photo
Why does the URL end with "?subcat=electronics-photo" rather than "/electronics-photo" ? Is it somehow related to the route definition?
routes.MapRoute(
"DisplayCategory",
"Home/ShowCategory/{category}/{tags}",
new { controller = "Home", action = "ShowCategory", category = "", tags = "" }
);
Any clues would be appreciated!
MVC routing tacks on a query string if it can't match one of the parameters in the route definition. Try adding subcat to your route:
routes.MapRoute(
"DisplayCategory",
"Home/ShowCategory/{category}/{tags}/{subcat}",
new { controller = "Home", action = "ShowCategory", category = "", tags = "", subcat = "" }
);
I'm just taking a stab but it looks like you're using the MVC framework.
In it every controller has an action, in this case ShowCategory, and by default they take a query string, in this case category. MVC purposefully abstracts the folder sturcture. I believe a MVC app will always have a URL of this type and depth domain.ext/Controller/Action?queryString.
I think the question may be whether or not the subcat is set to properly. A little more info/code might be hopeful.
Also sorry for not addressing the routing portion of your question.

Categories