Understanding MVC routing conventions - asp.net-mvc

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.

Related

I want to take into account double slashes (//) or more in url parameter value

I would like that if my URL is domain/page?param=//, $_GET[‘param’] == ‘//’
Actualy, $_GET[‘param’] == ‘/’
To put some context, my URL domain/page is in fact rewritten in domain/index.php?action=page where the action parameter holds my route and parameters.
Lets’s say my action parameter value is ‘route/subroute/param1/param2’. Each part of the string is removed one after another starting by the end. If a route is find (ie route/subroute exists in my route array), then I call the controller etc… and the deduced parameters are param1 and param2.
Now I would like to take double or more following slashes into account for 2 reasons :
let’s say I have optional parameters a, b, c, and they are like this in URL : ‘a//c’. Then it is interpreted as ‘a/c’ and so b=’c’ instead of b=’’.
If I have route//subroute, I want it to be interpreted as an error (‘route//subroute’ doesn’t exists nor ‘route/’) and display an error view instead of it becoming valid (‘route/subroute’) to avoid duplicate content SEO wise.
Some sites manage to keep the URL intact and display an error view when typing domain//somepage or domain/somepage//subpage, so I guess it is possible.
Thanks a lot!

Optional static value in attribute routing

I'm using attribute routing for a current project and in a few of the routes, I'm using some optional parameters. So for a URL like...
/detail/H40466/wood-to-wood-foundation-and-boxspring-frame-assembly
With its route definition like...
[Route("detail/{productName}/{applicationSlug?}")]
The wood-to-wood... is an optional parameter. What I'm wanting to do (if possible) is to have a static value only show up if the second parameter is present. Something like...
/detail/H40466/for/wood-to-wood-foundation-and-boxspring-frame-assembly
Where the word for is only part of the url when the last optional parameter is present. Is there any mechanism available to accomplish this beyond setting up another action that maps to that route?
You can define 2 different routes for the same action method. In that case, the "optional" parameter should be required for one route and not present on the other.
[Route("detail/{productName}/for/{applicationSlug}", Order = 1)]
[Route("detail/{productName}", Order = 2)]

MVC Routing bogus URLs allowed

I am currently using MVC Routing and MvcSiteMapProvider. I have just noticed something which I am unsure about:
[Route("mypath/{param1}/{param2}/{param3:int}/", Name = "myaction")]
[MvcSiteMapNode(Title = "My Thing", ParentKey = "myparent", Key = "myaction", PreservedRouteParameters = "param1, param2, param3")]
public ActionResult myaction(string param1, string param2, int param3)
{
mymodel model = gd.getmydata(param3);
var node = SiteMaps.Current.CurrentNode;
node.Title = model.name;
node.ParentNode.Title = location;
node.ParentNode.RouteValues["param"] = location;
return View(model);
}
When I test the url is how I designed it:
http://localhost:12345/mypath/param1/param2/param3
However if I bastardise the URL with drivel in param2 or param3 the view still resolves with no error. Only by changing mypath or param3 does it 404:
http://localhost:12345/mypath/drivel/param2/param3
http://localhost:12345/mypath/param1/drivel/param3
Am I missing something? Should I have parameter validation in the controller?
The tokens in the URL parameter of the route act like variables. The way you have your route configured, it will always match any URL starting with mypath/ and containing 3 additional segments. The route does not care what values are put into those segments, it will make them into route keys named param1, param2, and param3 containing whatever values are in the actual URL.
If you want the route only to match the URL /mypath/param1/param2/param3, you will need to use literal route segments.
[Route("mypath/param1/param2/param3/", Name = "myaction")]
But then, since your param3 segment is an integer value and your URL contains a string, it is difficult to tell what you are trying to achieve. If you declare it an integer, you must place an integer in the URL or you will get an error.
/mypath/param1/param2/123
Another thing to note if you are using literal segments in your URL is that they are not converted into route values, so there is no need to use PreservedRouteParamters in that case.
So, if you declare your route with variables, it should not be a surprise that any value works. If you want an explicit route, declare it with literal segments. If you want variables but want to restrict the route to a certain range of possible values, then you need to use route constraints.

Map Area, Controller and Action names to default values if empty

I am given 3 strings: area, controller and action. Some of them could have empty values, as there are default values defined in my routes. For example, if action is empty it will be mapped to Index.
I wish to pass these 3 strings through the routing of my application and get their actual values after any empty values where mapped to default values. Is this possible?
Maybe it is doable by doing something like this
RouteTable.Routes.GetRouteData(context)
but is there a way to do it without constructing a whole HttpContext object?
The first thing you will have to do is take your three strings and convert that into a URL that can be passed thru the routing system. How you do this depends upon what routes you have created. Let's assume you have the typical default route in your route table:
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller="Home", action = "Index", id = UrlParameter.Optional }
);
Then you would have to use the URL pattern "{area}/{controller}/{action}/{id}" to build up your URL. In your case, some of the strings could be empty, so your URL would not have all of the segments. For example, if Area="Admin" and Controller and Action are empty, your URL, based on this route, would be:
~/Admin
Of course, this raises the question of if you already know the routes in your system, why can't you just get the defaults by looking at the routes.
Once you have the URL that you want to test, you will need to create a mock of the HttpContentBase object and then run it thru RouteTable.Routes.GetDate(httpContext). There is straightforward boiler plate code to do this, since you really do not need to mock much of the constituent HttpRequestBase or HttpResponseBase classes. For example, see here for an example of mocking an HttpContextBase object using the Moq mocking framework.

Appending ?param= to mvc routes

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.

Resources