views:

308

answers:

2

Hi all, I am building a MVC application in asp.NET for a web portal. I have prepared a series of controllers, and mapped all the paths that don't macth to this to a Page controller, which will render the appropriate page.

My default route works like this:

routes.MapRoute(
  "Default",
  "{level1}/{level2}/{level3}",
  new { controller = "Page", action = "Index", level1 = "home", level2 = "", level3 = "" }
      );

But this has fixed width, it will accept only up to 3 levels. Moreover, I'd like to manage actions appended to the path, like "edit" and "delete". Is this possible?

company/about/who_we_are/staff -> Controller: Page, Action: Index, Parms: company/about/who_we_are/staff
company/about/who_we_are/staff/edit  -> Controller: Page, Action: Edit, Parms: company/about/who_we_are/staff
company/edit  -> Controller: Page, Action: Edit, Parms: company

Or is there a better way to model this? All the paths to the pages are in the database, so they change dynamically.

+1  A: 

As far as i know, you can use regular expressions to express what the routes can look like (see the bottom code section here). With this, it should be possible to make a regex-string that can take an undetermined number of sub-sections ("forward-slashe and text/number-groups"). You can then parse the URL string in your application and retrieve the appropriate section.

I am, however, not capable of writing this regex-string by myself without spending hours, so someone else can probably help you there. :-)

Arve Systad
+3  A: 

You can use a wild-card route:

"{*data}"

take a look a this SO: ASP.net MVC custom route handler/constraint


simple possible solution:

(not tested but...)

The route:

routes.Add(new Route
                           (
                           "{*data}",
                           new RouteValueDictionary(new {controller = "Page", action = "Index", data = ""}),
                           new PageRouteHandler()
                           )
                );

The handler would look like:

public class PageRouteHandler : IRouteHandler
{
    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new PageHttpHandler(requestContext);
    }
}

class PageHttpHandler : MvcHandler
{
    public PageHttpHandler(RequestContext requestContext)
        : base(requestContext)
    {
    }

    protected override void ProcessRequest(HttpContextBase httpContext)
    {
        IController controller = new PageController();

        ((Controller)controller).ActionInvoker = new PageActionInvoker();

        controller.Execute(RequestContext);
    }
}

class PageActionInvoker : ControllerActionInvoker
{
    protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
    {
        string data = controllerContext.RouteData.GetRequiredString("data");
        string[] tokens = data.Split('/');


        int lenght = tokens.Length;

        if (lenght == 0)                   
            return new NotFoundResult();

        if (tokens[tokens.Length - 1] == "edit")
        {
            parameters["action"] = "edit";
            lenght--;
        }

        for (int i = 0; i < length; i++)
            parameters["level" + (i + 1).ToString()] = tokens[i];

        return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);
    }
}
CD
Seems like it won't allow a simple way to append the "edit" action to my paths :( but that's a problem of the MVC engine itself. Maybe I'll just prepend the action instead...
Palantir