I have a web application using MVC 2 Preview 2 and after all routes are registered, I need to wrap each route in a decorator further down the chain. The problem is, doing so breaks routing. What ends up happening is the GetVirtualPath method will match falsely for other areas in the application (I'm using single-project areas). It doesn't matter if the decorator does anything useful or not. Using the following passthrough is all you need to break it.
public class RouteDecorator: RouteBase
{
readonly RouteBase _route;
public RouteDecorator(RouteBase route)
{
_route = route;
}
public override RouteData GetRouteData(HttpContextBase context)
{
return _route.GetRouteData(context);
}
public override VirtualPathData GetVirtualPath(RequestContext context, RouteValueDictionary values)
{
return _route.GetVirtualPath(context, values);
}
}
I'm assigning the decorator in a simple loop after all routes are registered.
var routes = RouteTable.Routes;
for (var i = 0; i < routes.Count; i++)
{
routes[i] = new RouteDecorator(routes[i]);
}
How can I safely insert a decorator without breaking routes and areas?
I have a reproduction solution available to download here. In the reproduction, the route decorator is commented out. Commenting it back in will break routing and the first dummy area's routing data will match the links that normally will correctly match only the corresponding namespace.
Routing enables us to define a URL pattern that maps to the request handler. This request handler can be a file or class. In ASP.NET Webform application, request handler is . aspx file, and in MVC, it is the Controller class and Action method.
The MVC routing has 3 parameters. The first parameter determines the name of the route. The second parameter determines a specific pattern with which the URL matches. The third parameter is responsible for providing default values for its placeholders.
Every ASP.NET MVC application must configure (register) at least one route in the RouteConfig class and by default, the ASP.NET MVC Framework provides one default route. But you can configure as many routes as you want.
MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); Inside the call to UseEndpoints() , we use the MapControllerRoute() method to create a route by giving the name default . MVC configures the default route template as {controller=Home}/{action=Index}/{id?} .
I think it's down to the way areas use the DataTokens dictionary to store the area/namespace information. As you are inheriting from RouteBase you probably need to implement the IRouteWithArea interface too as you don't have the DataTokens that a Route has.
The ActionLink helper seems to indirectly call this hence the need for this new interface:
public static string GetAreaName(RouteBase route)
{
IRouteWithArea area = route as IRouteWithArea;
if (area != null)
{
return area.Area;
}
Route route2 = route as Route;
if ((route2 != null) && (route2.DataTokens != null))
{
return (route2.DataTokens["area"] as string);
}
return null;
}
[Edit - 2009-11-12] I believe the following will fix the issue, as the decorator seems to end up wrapping the route more than once:
Additional property on decorator:
public RouteBase InnerRoute
{
get
{
return _route;
}
}
Interface implementation:
public string Area
{
get
{
RouteBase r = _route;
while (r is RouteDecorator)
r = ((RouteDecorator) r).InnerRoute;
string s = GetAreaToken(r);
if (s!= null) return s;
return null;
}
}
private string GetAreaToken(RouteBase r)
{
var route = r as Route;
if (route != null && route.DataTokens !=null && route.DataTokens.ContainsKey("area"))
{
return (route.DataTokens["area"] as string);
}
return null;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With