MVC: Routing Helpers

JanL@kentico.com[email protected] Czech RepublicMember, Administrator, Kentico Staff admin
edited February 2017 in Back-end Development

Note: This post was rewritten to better reflect the fact that the sitemap changes very often.

In regards to routing, we assume that an MVC developer often faces the following business requirements:

  • The URLs of the app must fully reflect the hierarchy of the Kentico Cloud sitemap.
  • The business users should be able to do the following without the need to contact developers:
    • Add new content items, edit and delete the existing ones
    • Add content items of more than one content type to the same sitemap location
    • Re-organize the hierarchy of the sitemap

We've done some initial research on the topic that we want to share with you. Feel free to discuss your thoughts below!

Prerequisites

As a prerequisite to the below approach, there has to be a new Deliver API property 'system.sitemap_paths' implemented in Deliver. It should contain full, SEO-friendly paths of a content item. The MVC app should be able to request all items that are located in that path: /items?system.sitemap_paths[contains]=<nodePath>. Also, the solution would require a new API endpoint for getting the sitemap nodes '/sitemapnodes'.

This is what the 'items' API endpoint could return and what the MVC app could do about it:

  • If the node has no items associated to it:

    • The MVC app could request the direct children of the node (API: /sitemapnodes?pathofparent=<nodePath>&depth=1). If at least one of them has any content items, then it could render the listing of content items of those direct descendant nodes.

      • It could be done with a combination of both /sitemapnodes?pathofparent=<nodePath>&depth=1 and /items?system.sitemap_paths[contains]=<nodePath>.
      • Alternatively, a new operator 'childrenof' could help: /items?system.sitemap_paths[childrenof]=<nodePath>)
      • Another way is that the API could return some of the information upfront. In the response with a node there could also be a boolean property telling us whether the node has descendants and/or content items associated to those descendants.
      • The most effective solution would be that /items?system.sitemap_paths[contains]=<nodePath> immediately returned an array of items of descendant nodes. The downside is that such a convention would be hard to change in the future.
    • If there is no descendant node or no items associated to descendant nodes, the MVC app should respond a 404 page to the user.

  • Should there be just one item associated to that node, that item would be returned.

  • If the node has multiple items associated to it, all of those items would be returned.
    • Note: This is how the existing /items?system.sitemap_location[contains]=<nodeCodeName> works

The following approach also has another prerequisite - strong typing of content types.

Routing

In order to fully reflect the sitemap paths, the MVC app needs to have such default 'wildcard'route.

routes.MapRoute(
    name: "default",
    template: "{*path}",
    defaults: new { controller = "Content", action = "Show", path = "Home" }
    );

Other routes could also be defined to allow for other kinds of actions.

routes.MapRoute(
    name: "modularContentDepth",
    template: "d-{modularContentDepth}/{*path}",
    defaults: new { controller = "Content", action = "ModularContentDepth", modularContentDepth = 1, path = "Home" }
    );

The MVC routing engine limits us to put the 'd-{modularContentDepth}' in front of the catch-all '{*path}' parameter. This limitation could be potentially worked around with a custom logic inside of the controller. That logic would parse the ending of the path.

Controllers

The possibility of having items of various content types in one place also means we should have a single 'entry' controller class for all types of items. The class should request content off the API endpoint and dynamically cast all particular items to their types (based on the 'system.type' property).

Views

The presentation layer of the MVC app should fully rely on the functionality of MVC display templates. Therefore, the default view of the Content controller should contain minimum to no markup.

Nothing deters us from putting the common header and footer markup to the standard '_Layout.cshtml' file.

Link generation

An ordinary HtmlHelper syntax could be used to generate links:

@Html.ActionLink("Go to Products", "Show", productsPath)

@Html.ActionLink("Go to Products", "ModularContentDepth", 2,  productsPath)

Questions

  • How often does the hierarchy of the sitemap change during the project development? Why?
  • How often does it change after going live? Why?
  • Can you see any significant limitations in the aforementioned approach?
  • Is the approach overly dynamic and declarative? As a developer, would you like to have more control over the routing? Why?
  • Do your MVC sites work mainly with content data? Or, does working with static content mean only a small portion of the whole project?

Feel free to discuss the topic here! We'd love to hear if this is what you'd use in your projects. Then, we could write an article and discuss how we can improve the Deliver .NET SDK, together with the community.

Comments

  • JanL@kentico.com[email protected] Czech RepublicMember, Administrator, Kentico Staff admin

    I've completely rewritten this post because the original one built on a hypothesis that:

    • the sitemap changes very rarely over time
    • the developer needs to work with the sitemap data in the code

    Now, the article builds on the fact that content editors should be able to move things around without developers. And it shows how an MVC app could be architected to suit that need.

  • EmmanuelTisseraEmmanuelTissera AustraliaMember

    Hi @JanLenoch ,

    I like the approach of been able to query the API when a request is made. But we would need to think of the performance implications here and implement some caching in the future. But I would love to see it working first.

    Will the template (i.e. view) for that particular content type be returned when /items?system.sitemap_paths[contains]=<nodePath> is called? I'm trying to figure out how the correct view will be selected in the controller.

    Cheers,
    Emm

  • JanL@kentico.com[email protected] Czech RepublicMember, Administrator, Kentico Staff admin
    edited February 2017

    Hi @EmmanuelTissera ,

    The API call you've mentioned could return the ordinary JSON response that contains items. The selection of a proper view could be handled entirely by the MVC display templates. In MVC you can simply put e.g. an Article.cshtml file to ~/Views/Shared/DisplayTemplates. Then, every time you pass some strongly typed model to a view, MVC looks for display templates for each type participating in the model (view model). For each part of the model it renders HTML according to the display template (for each Article item it would use the Article.cshtml). The conventional view file (a view named according to the controller name) could be just left empty.

    Interesting point with the caching. Have you had a chance to read my post on that topic? I'm mainly concerned about whether you observed any performance issues in your projects.

    Thanks!

  • petrs@kentico.com[email protected] Eindhoven, NLMember, Administrator, Kentico Staff admin

    regarding d-{modularContentDepth}/{*path} - I'd definitely do that via a query parameter

  • JanL@kentico.com[email protected] Czech RepublicMember, Administrator, Kentico Staff admin

    I've published a comprehensive article on the topic in Kentico Cloud blog.

Sign In or Register to comment.