views:

2423

answers:

1

I need to implement an MVC site with urls per below:

  • category1/product/1/wiki
  • category1/product/2/wiki
  • category1/sub-category2/product/3/wiki
  • category1/sub-category2/sub-category3/product/4/wiki
  • etc. etc.

where the matching criteria is that the url ends with "wiki".

Unfortunately the below catch-all works only in the last part of the url:

routes.MapRoute("page1", // Route name
                "{*path}/wiki", // URL with parameters
                new { controller = "Wiki", action = "page", version = "" } // Parameter defaults

I have not had the time to go through the MVC extensibility options so I was wondering what are the possible choices for implementing this? Any sample/example would be just fantastic!

+6  A: 

As you mentioned, the catch-all parameter can only appear at the end of a route - the code that you have posted will throw a run-time error and give you the yellow screen of death if you even try to run your application.

There are several extensibility points for building custom routing scenarios. These are - Route, RouteBase, and IRouteHandler.

You can create a generated list of routes to handle by extending RouteBase. Typically you would follow the pattern of having a constructor that takes in a resource (controller name), and then assigning it a list of routes it was responsible for, and then mapping it in your global.asax. Here is some example code you can build from:

public class MyRoute : RouteBase
{
    private List<Route> _routes = new List<Route>();

    public MyRoute(string resource)
    {
        // make a Resource property, not shown in this example
        this.Resource = resource;

        // Generate all your routes here
        _routes.Add(
            new Route("some/url/{param1}",
            new McvRouteHandler {
                Defaults = new RouteValueDictionary(new {
                    controller = resource,
                    action = actionName
                }),
            Constraints = new RouteValueDictionary()
        );
        _routes.Add(...); // another new route   
    }

    public override RouteData GetRouteData(HttpContextBase context)
    {
        foreach (var route in _routes)
        {
            var data = route.GetRouteData(context);
            if (data != null)
            { 
                return data;
            }
        }
        return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext context, RouteValueDictionary rvd)
    {
        foreach (var route in _routes)
        {
            var path = route.GetVirtualPath(context, rvd);
            if (path != null)
            { 
                return path;
            }
        }
        return null;
    }
}

To use your routing class, do a routes.Add(new MyRoute("page1")); in your Global.asax.

If you need even more control, you can implement an IRouteHandler, and instead of creating MvcRouteHandlers() for your routes as shown in the above example, use your own IRouteHandler. This would allow you to override the logic of selecting the controller from the request data.

The entire framework is extremely extensible, but you would need to learn quite a bit in order to do it properly. I would suggest simply rearranging your URL's if possible to take advantage of the catch-all parameter if you can.

womp