Zayar
Zayar

Reputation: 347

Asp.net mvc 3.0 tree structure page custom routing

I want to map all CMS pages url to single controller(PageController) and action(Details).

How can I create custom routing to map all these urls?

/teacher
/teacher/kindergarten
/teacher/kindergarten/1
/teacher/primary
/teacher/primary/english
/teacher/primary/language
/teacher/primary/language/chinese
/teacher/primary/math
/teacher/primary/science
/parent
/parent/kindergarten
/parent/primary1-3
/parent/primary4-6
/leader
/leader/kindergarten
/leader/kindergarten/1

Upvotes: 1

Views: 490

Answers (2)

Nocher
Nocher

Reputation: 11

Previous proposed solution is working only for small amount of pages. Because according to the code:

application generate and register Route for each of site page. In result we have at least same amount of routes as pages in our site. As you probably know RouteModule have to check route by route each of them to find first right one and execute correct handler, controller, action, view...

There are two other way to solve this:

  • You can create a class that derives from RouteBase and implement the properties and methods that you need: split url to segments, determinate current page fill RouteValueDictionary with pageid, path, parents etc

  • You can customize UrlRewriteModule with custom rewrite provider. Idea to transform all requests url from tree base structure to mvc default route:

    {controller}/{action}/{id}?path=parentlevel1/parent2/parent3/....

90% -same code for both variants could be prepared. that solution also could be useful when you have different controllers, correct one we could determinate by current page (by page data: type)

Upvotes: 1

Sam Huggill
Sam Huggill

Reputation: 3126

If you have these URLs in a database you could map the routes when the application starts up:

  var pages = siteDB.Pages.ToList();
  string pagePath = "";
  foreach (var page in pages)
  {
    routeVals = new RouteValueDictionary();
    constraints = new RouteValueDictionary();

    routeVals.Add("controller", "page");
    routeVals.Add("action", "details");

    constraints.Add("path", "[a-zA-Z0-9\\-]*");

    // any child pages? must add these routes before their parent pages.
    var childPages = siteDB.Pages.Where(p => p.ParentPageId == page.PageId).ToList();
    foreach (var childPage in childPages)
    {
      pagePath = BuildPath(childPage);
      RouteTable.Routes.Add(new Route(pagePath, new MvcRouteHandler())
      {
        Defaults = routeVals,
        Constraints = constraints,
        DataTokens =
            new RouteValueDictionary {
            { "pageid", childPage.PageId },
            { "path", pagePath }
          }
      });

      // Any further child pages? (Only 3 levels supported)
        var childSubPages = siteDB.Pages.Where(p => p.ParentPageId == childPage.PageId).ToList();
        foreach (var childSubPage in childSubPages)
        {
            pagePath = BuildPath(childSubPage);
            RouteTable.Routes.Add(new Route(pagePath, new MvcRouteHandler())
            {
                Defaults = routeVals,
                Constraints = constraints,
                DataTokens =
                    new RouteValueDictionary {
                { "pageid", childSubPage.PageId },
                { "path", pagePath }
              }
            });
        }
    }

This code takes the pages from a database where they are linked by parent id.

Here's the BuildPath function which generates a full path to each page:

public static string BuildPath(Page page)
{
  if (page.ParentPageId == 1)
  {
    return page.PageKey;
  }
  else
  {
    SiteDataEntities siteDB = new SiteDataEntities();
    string path = page.PageKey;
    Page parent = siteDB.Pages.Find(page.ParentPageId);
    while (parent != null)
    {
      path = parent.PageKey + "/" + path;
      parent = siteDB.Pages.Find(parent.ParentPageId);
      if (parent.PageKey == "home") break;
    }
    return path;
  }
}

Upvotes: 1

Related Questions