Why do route decorators break routing in ASP.NET MVC 2?
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 Rout开发者_如何学GoeDecorator(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.
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;
}
}
What happens if you decorate the Route class instead of RouteBase?
Think of something like this:
public class RouteDecorator: Route
{
readonly Route _route;
public RouteDecorator(Route 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 also recommend checking out System.Web.Routing.dll with Reflector, it might give you an insight of what is happening.
And also, what happens, if you do this:
var routes = RouteTable.Routes.ToList();
RouteTable.Routes.Clear();
//or, alternatively, if the above doesn't work:
//RouteTable.Routes = new RouteCollection();
foreach (var r in routes)
{
RouteTable.Routes.Add(new RouteDecorator(r));
}
I hope very much that it helps.
精彩评论