Contribute to Docs Contribute to Docs

Advanced Routing

Sometime's you'll want to implement pages that take custom parameters and then performs some business logic, but are still positioned within the sitemap like a regular content page. Luckily this is not a problem at all since Piranha only rewrites the part of the requested URL that can be matched to a permalink and leaves the rest for your application to handle.

Let's for example say we wanted to implement a page in our site that shows details about a product, but the product data is in fact in another system.

Create The Page Type

We're going to start by creating a new Page Type that has it's own unique Route configured to handle our product details. We're also going to disable blocks as we don't want a lot of extra content taking up space.

using Piranha.AttributeBuilder;
using Piranha.Models;

[PageType(Title = "Product Page", UseBlocks = false)]
[PageTypeRoute(Title = "Default", Route = "/productpage")]
public class ProductPage : Page<ProductPage>
{
    public MyProduct SelectedProduct { get; set; }

    ...
}

This page type could of course contain any number of Regions, Fields or Blocks you want it to have.

Create The Controller Action

Next up we need to create an Action that handles our new route. This action can be added to any controller you want as long as the route matches the one specified on the Page Type.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Piranha;

public class MyController : Controller
{
    private readonly IApi _api;
    private readonly IProductApi _productApi;

    public MyController(IApi api, IProductApi productApi)
    {
        _api = api;
        _productApi = productApi;
    }

    [Route("/productpage/{productId?}")]
    public async Task<IActionResult> ProductPage(Guid id, string productId = null)
    {
        if (!string.IsNullOrEmpty(productId))
        {
            // Get the page from Piranha
            var model = await _api.Pages.GetByIdAsync<ProductPage>(id);

            // Get the selected product from our external system
            model.SelectedProduct = _productApi.GetProductById(productId);

            return View(model);
        }
        else
        {
            // TODO: Someone came here without a product id, handle it gracefully.
        }
    }
}

Since the routing of Piranha doesn't know that we want a product id the page will be matched even when no product id is requested. For this reason this should always be handled. Another way to handle it is to create an Alias for the permalink of the page (with no product id) that performs a redirect to another page.

Requesting The Page

Now let's say we publish our product detail page in our site on the permalink /products/details and we have some kind landing page for our products at /products, we can now request it by calling.

  • GET /products/details/the-first-product
  • GET /products/details/the-second-product

Both of these requests will end up on the same page in Piranha with the additional route parameter available in the actions in productId.