Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent Url.RouteUrl(...) from inheriting route values from the current request

Lets say you have an action method to display products in a shopping cart

 // ProductsController.cs  public ActionMethod Index(string gender) {        // get all products for the gender  } 

Elsewhere, in a masthead that is displayed on every page you are using Url.RouteUrl to create HREF links to other pages on the site :

 <a href="<%= Url.RouteUrl("testimonials-route", new { }) %>" All Testimonials </a> 

This testimonials-route is defined in global.ascx by the first route below. Notice that the above call to RouteUrl does not provide a gender, but the route is defined with a default of 'neutral' so we'd expect Testimonials.Index("neutral") to be called.

 routes.MapRoute(   "testimonials-route",   "testimonials/{gender}",   new { controller = "Testimonials", action = "Index", gender = "neutral" },   new { gender = "(men|women|neutral)" }  );  routes.MapRoute(   "products-route",   "products/{gender}",   new { controller = "Products", action = "Index", gender = (string)null },   new { gender = "(men|women|neutral)" }  ); 

If someone visits the page /products/women we get an HREF to /testimonials/women If someone visits the page /products then we get an empty HREF (the call to RouteUrl returns null).

But that doesn't make sense does it? testimonials-route is supposed to default to 'neutral' for us if we don't provide a route value for it?

What turns out is happening is that Url.RouteUrl(routeName, routeValues) helper extension will first look in its routeValues parameter for a gender route value and if it doesn't find it in that dictionary it will look at the current URL that we're on (remember that Url is a UrlHelper object which has the context of the current request available to it).

This has a possibly nice effect of giving us a link to men's testimonials if we're on a mens product page, but that probably isnt what we want if we haven't passed a value in the RouteUrl call, and explicitly specified 'neutral' as a default in the global.asax.cs file.

In the case where we visited /products/ we triggered the 'products-route' route and the Products(null) method was called. The call to Url.RouteUrl() actually inherits THIS null value for gender when we're creating a URL using testimonials-route. Even though we have specified a default for gender in 'testimionials-route' it still uses this null value which causes the route to fail and RouteUrl returns null. [note: the route fails because we have a constraint on (men|women|neutral) and null doesn't fit that]

It actually gets more scary - in that 'controller' and 'action' can be inherited in the same way. This can lead to URLs being generated to completely the wrong controller even when calling RouteUrl(...) with an explicit route name that has a default controller.

In this case once you've figured it out you can fix it quite easily in numerous ways, but it could in other cases cause some dangerous behavior. This may be by design, but its definitely scary.

like image 450
Simon_Weaver Avatar asked Jul 18 '09 20:07

Simon_Weaver


People also ask

How do I set the default route in .NET Core Web API?

Right click your web project -> Select Properties -> Select the Debug tab on the left -> Then edit the 'Launch Url' field to set your own default launch url.

How can we configure conventional routing?

Conventional or Traditional Routing also is a pattern matching system for URL that maps incoming request to the particular controller and action method. We set all the routes in the RouteConfig file. RouteConfig file is available in the App_Start folder. We need to register all the routes to make them operational.

What is route controller?

Camel uses a RouteController for managing the lifecycle of all the routes. The controller is mainly in use during starting up the routes when Camel startup. The controller is handling which routes, and in which order they should be started.

What is routes Maproute in MVC?

In MVC, routing is a process of mapping the browser request to the controller action and return response back. Each MVC application has default routing for the default HomeController.


1 Answers

My solution was this :

An HtmlExtension helper method :

    public static string RouteUrl(this UrlHelper urlHelper, string routeName, object routeValues, bool inheritRouteParams)     {         if (inheritRouteParams)         {             // call standard method             return urlHelper.RouteUrl(routeName, routeValues);         }         else         {             // replace urlhelper with a new one that has no inherited route data             urlHelper = new UrlHelper(new RequestContext(urlHelper.RequestContext.HttpContext, new RouteData()));             return urlHelper.RouteUrl(routeName, routeValues);         }     } 

I can now do :

Url.RouteUrl('testimonials-route', new { }, false) 

and know for sure it will always behave the same way no matter what the context.

The way it works is to take the existing UrlHelper and create a new one with blank 'RouteData'. This means there is nothing to inherit from (even a null value).

like image 139
Simon_Weaver Avatar answered Oct 06 '22 13:10

Simon_Weaver