Appending ?param= to mvc routes - asp.net-mvc

Some MVC sites have querystring params appended to the route Url (of which I noticed StackOverflow does), such as:
https://stackoverflow.com/questions/tagged/java?page=9802&sort=newest&pagesize=15
What are the advantages of having the parameters as more conventional ?querystring params, rather than /param/values/ ?
Also, how are these params appended to routes that have been set up? I'm familiar with setting up mvc routes with params like "users/details/{id}" etc. but don't know how to configure routes for use with 1 or more ?params as per the example url above?

Query string parameters are useful when you have multiple optional parameters and don't want to include default values for non-specified parameters just to satisfy a path.
And you don't have to do anything special to include these parameters in a rendered URL.
Take the following route for example:
routes.MapRoute
(
"QuestionsTagged",
"questions/tagged/{tag}",
new { controller = "Questions", action = "Tagged" }
);
If you render a link to that route using:
Url.RouteUrl
(
"QuestionsTagged",
new
{
tag = "java",
page = 9802,
sort = "newest",
pagesize = 15
}
)
...then the routing engine is smart enough to see that the route contains a parameter named tag and that the passed route values object also has something named tag so it uses that value in the route.
Any provided route values that don't have corresponding parameters in the route (page, sort and pagesize in this case) get tacked on as query string parameters. So the Url.RouteUrl call above would return /questions/tagged/java?page=9802&sort=newest&pagesize=15.
And your action method can explicitly list these parameters in its signature (promotes readability and maintainability) or you can access them via Request.QueryString.
public class QuestionsController : Controller
{
// I can explicitly list the parameters in my signature and let routing do
// its magic, like this...
public ViewResult Tagged(string tag, int? page, int? pagesize)
{
// ...or I can grab parameters like this:
string sort = Request.QueryString["sort"];
return View();
}
}
Note that the parameters to the action method do not have to match the parameters specified in the route. (In the route, I only specified tag, but the action method's signature lists tag, page, and pagesize.) However, any parameter of the action method that is not also a parameter of the route must be a reference or nullable type.

I've normally seen paging and filtering data be passed as querystring parameters since it gives information to the user in the URI. It is also normally harmless if a user alters this data since it will just filter the data you see on the page. Any sensitive data is normally posted so as it is not as easily seen or modified, but I would argue to keep your URI's clean and use quesrystrings as little as possible.
You don't need to do anything special when specifying routes to be able to handle quesrystrings. They will just be extra data that is passed to your action. On your action you will need to do some work to handle the data though. Using your querystring above you will have to specify the querystring names as the parameter names and then whatever datatype you are expecting.
public ActionResult Index (int page, string sort, int pagesize)
In this example, page will be the value of 9802, sort will be "newest" and pagesize will be 15.

Related

What is the purpose of MVC passing the current "controller" and "action" as parameter values

I had to pass an extra parameter with my action links to indicate where they came from (as I needed to change a back link in the pages accordingly).
As it was a controller name, I decided to name it controller.
e.g. a sample link might be:
#Html.ActionLink(item.Name, "Options", "Questionnaire", new {
id = item.QuestionnaireId,
controller = "templates" }, null)
The receiving action in QuestionnaireController looked like:
public ActionResult Options(int id, string controller)
When the action was hit I noticed the controller value was not template, but instead was the name of the current controller (i.e. QuestionnaireController).
As an experiment I added an action parameter e.g.:
public ActionResult Options(int id, string controller, string action)
the action value was the current action too (i.e. Options).
My work-around for this was simply to rename my parameter to source, but why does MVC bother to map the names controller and action to action parameters? I assume that would apply to any/all Route Mapping values, but what is the purpose of this?
Why does MVC bother to map the names controller and action to action parameters?
I believe it's done as part of the QueryStringValueProvider or one of the other ValueProviders (maybe the RouteDataValueProvider). ASP.Net MVC uses Convention over Configuration, so the framework uses the values provided to populate method parameters. The Controller name, Action name and even the Area name are all values provided in the Url.
I assume that would apply to any/all Route Mapping values, but what is the purpose of this?
The ValueProvider is used for Routing data to determine the matching route to use, it also happens to be the same object that provides the data to populate method parameters. The side affect you are experiencing is most likely not a feature they were trying to implement.
The DefaultModelBinder.GetValue uses the ValueProviders to locate a value and bind it to the model (or method paramater).

Understanding MVC routing conventions

I am looking at an example of MVC Route and it looks like following:
routes.MapRoute("Cuisine", "cuisine/{name}", new
{ controller = "Cuisine", action = "Search", name = ""})
I am just trying to dissect what each element stands for here:
"cuisine/{name}"
Does this part say that if a request comes with URL starting from word cuisine and there is a second string parameter and route it to this particular Route and consider second part of URL as parameter name?
{ controller = "Cuisine", action = "Search", name = ""})
If nothing is passed into the parameter then please use empty string as default and if some value is passed for name parameter then use that value?
"cuisine/{name}"
. Does this part say that if a request comes with URL starting from word cuisine and there is a second string paramter and route it to this particular Route and consider second part of URL as parameter name?
Yes, that is correct.
{ controller = "Cuisine", action = "Search", name = ""})
If nothing is passed into the parameter then please use empty string as default and if some value is passed for name parameter then use that value?
Yes, that is also correct. However, it is usually more sensible to specify an optional parameter as UrlParameter.Optional.
Basically, there are 2 (important) parts to a route configuration.
There is a match specification which always consists of the URL parameter and may also include constraints, optional, and required values. A required value is one where you don't specify any default for a route key and also include it as a route segment. If a route doesn't fit the criteria of the match, the framework will attempt the next route in the configuration until it finds a match (or not). It is helpful to think of it as a switch case statement.
Then there is a route value specification. This is a combination of the default values and any incoming URL segments that may override or add to them. Note that constant parts of the URL are not included in the route values. The only reason why "Cuisine" is inserted into the route values in your example is because it is defined as a default route value.

Routing values to the MVC Player function?

In a Composite C1 application, I am trying to pass values from the URL to the MVC Player function, but I have trouble because the values are part of the path and not in the query string.
The URL looks like this:
/AuctionDetailsGallery/3624734/Test-Versteigerung-2
AuctionDetailsGallery is a Composite C1 Page which includes the MvcPlayer function.
3624734 is the (dynamic) ID, "Test-Versteigerung-2" is a userfriendly name
The MvcPlayer is then supposed to call
/AuctionViewer/FilterGalleryPositions
("AuctionViewer" is the controller and "FilterGalleryPositions" the action.)
The ID has to be passed to the action, but under a different name ("SelectedAuctions").
So essentially, if the user calls
/AuctionDetailsGallery/3624734/Test-Versteigerung-2
I want to render the MVC action
/AuctionViewer/FilterGalleryPositions?SelectedAuctions=3624734
How can I do this?
I set the MvcPlayer path to "/AuctionViewer/FilterGalleryPositions" and played around with the routes, but I always get the message
The controller for path '/3624734/Test-Versteigerung-2' was not found
or does not implement IController.
That's because the Render function checks for PathInfo and replaces the Path I set with the PathInfo if available. I guess it would be more useful if the PathInfo was appended, but I am unsure how to route my values with the current MVC Player implementation.
If I understand your question correctly I believe you are asking about routes.
With Attribute routing simply declare the route over the controller action (assuming controller name is AuctionViewerController):
[Route("AuctionDetailsGallery/{selectedAuctions}/Test-Versteigerung-2")]
public ActionResult FilterGalleryPositions(int selectedAuctions)
{
...
}
With a routetable you might define something like this:
routes.MapRoute(
name: "AuctionDetails",
url: "AuctionDetailsGallery/{selectedAuctions}/Test-Versteigerung-2",
defaults: new { controller = "AuctionViewer", action = "FilterGalleryPositions" }
);

How to route legacy type urls in ASP.NET MVC

Due to factors outside my control, I need to handle urls like this:
http://www.bob.com/dosomething.asp?val=42
I would like to route them to a specific controller/action with the val already parsed and bound (i.e. an argument to the action).
Ideally my action would look like this:
ActionResult BackwardCompatibleAction(int val)
I found this question: ASP.Net MVC routing legacy URLs passing querystring Ids to controller actions but the redirects are not acceptable.
I have tried routes that parse the query string portion but any route with a question mark is invalid.
I have been able to route the request with this:
routes.MapRoute(
"dosomething.asp Backward compatibility",
"{dosomething}.asp",
new { controller = "MyController", action = "BackwardCompatibleAction"}
);
However, from there the only way to get to the value of val=? is via Request.QueryString. While I could parse the query string inside the controller it would make testing the action more difficult and I would prefer not to have that dependency.
I feel like there is something I can do with the routing, but I don't know what it is. Any help would be very appreciated.
The parameter val within your BackwardCompatibleAction method should be automatically populated with the query string value. Routes are not meant to deal with query strings. The solution you listed in your question looks right to me. Have you tried it to see what happens?
This would also work for your route. Since you are specifying both the controller and the action, you don't need the curly brace parameter.
routes.MapRoute(
"dosomething.asp Backward compatibility",
"dosomething.asp",
new { controller = "MyController", action = "BackwardCompatibleAction"}
);
If you need to parametrize the action name, then something like this should work:
routes.MapRoute(
"dosomething.asp Backward compatibility",
"{action}.asp",
new { controller = "MyController" }
);
That would give you a more generic route that could match multiple different .asp page urls into Action methods.
http://www.bob.com/dosomething.asp?val=42
would route to MyController.dosomething(int val)
and http://www.bob.com/dosomethingelse.asp?val=42
would route to MyController.dosomethingelse(int val)

How do you use querystrings with ASP.NET routing?

The new ASP.NET routing is great for simple path style URL's but if you want to use a url such as:
http://example.com/items/search.xhtml?term=Text+to+find&page=2
Do you have to use a catch all parameter with a validation?
You can match querystring parameters with routes as well, if you want to just capture everything you need to add a parameter like so:
{*contentUrl}
Which will populate the rest of the url into that variable.
Any view data items that are not listed in the route are automatically mapped to the querystring, so if you map "items/search.xhtml" to an action:
Search(string term, int page)
Then you should get the results you are looking for.
I was also having trouble passing an encoded URL to a route as a route parameter.
You can't use url encoded chars in a URL, but you can in a query string.
Therefore I needed my route to also have a query string element to it.
Say I have a route:
MapPageRoute("myroute", "myroute/{x}", "~/routehander.aspx")
But I want it in the form of:
http://mywebsite.com/myroute/{x}?url=myurl
We can do this:
Dim x as integer = 12
Dim rvd As New Routing.RouteValueDictionary
rvd.Add("x", x)
rvd.Add("url", Server.UrlEncode("/default.aspx"))
HttpContext.Current.ApplicationInstance.Response.RedirectToRoutePermanent("myroute", rvd)
This would redirect us to the following url:
http://mywebsite.com/myroute/12?url=%252fdefault.aspx
You can still use Request.QueryString["some_value"];

Resources