Partial Routing with Episerver 7 and Web Forms

Saw this post on Episerver World about routing and friendly URLs and in that case external data. I had done something similar so I simplified and extracted into an Alloy project.

Joel's post about routing was a great starting point for me.

Some example data:

namespace EPiServer.Templates.Alloy.Models
{
    using System.Collections.Generic;

    public class ItemRepository
    {
        public static List<Item> GetAll()
        {
            return new List<Item>
            {
                new Item { Id = 458, Title = "Example item", UrlName = "example-item" },
                new Item { Id = 500, Title = "Funky item", UrlName = "funky" },
                new Item { Id = 899, Title = "Test item with id 899", UrlName = "this-is-an-awesome-urlname" }
            };
        }

        public class Item
        {
            public int Id { get; set; }
            public string Title { get; set; }
            public string UrlName { get; set; }
        }
    }
}

I then created to really simple page types, ItemPage and ItemListPage. I wanted to use the listing page's URL name as the first part of all "Item" custom friendly names. Therefore I published the pages like this:

Edit Mode screenshot

The listing page repeater:

<asp:Repeater runat="server" ID="ItemsRepeater"
        ItemType="EPiServer.Templates.Alloy.Models.ItemRepository.Item">
    <ItemTemplate>
        <%--We could attach a custom UrlRewriter but it's easier to just output
            the custom friendly URL by hand like this--%>
        <h2><a href="./<%#: Item.UrlName%>/"><%#: Item.Title%></a></h2>
    </ItemTemplate>
</asp:Repeater>

Code-behind for the list page including the partial router and router registration:

namespace EPiServer.Templates.Alloy.Views.Pages
{
    using System.Linq;
    using System.Web.Routing;
    using EPiServer.Framework;
    using EPiServer.Framework.Initialization;
    using EPiServer.Templates.Alloy.Models;
    using EPiServer.Templates.Alloy.Models.Pages;
    using EPiServer.Web.Routing;
    using EPiServer.Web.Routing.Segments;

    public partial class ItemListPageTemplate : SiteTemplatePage<ItemListPage>
    {
        protected override void OnLoad(System.EventArgs e)
        {
            base.OnLoad(e);
            ItemsRepeater.DataSource = ItemRepository.GetAll();
            ItemsRepeater.DataBind();
        }

        public class ItemListPageRouter : IPartialRouter<ItemListPage, ItemPage>
        {
            public object RoutePartial(ItemListPage content, SegmentContext segmentContext)
            {
                var nextSegment = segmentContext
                       .GetNextValue(segmentContext.RemainingPath);
                var urlSegment = nextSegment.Next;

                if (string.IsNullOrEmpty(urlSegment))
                {
                    return null;
                }

                ItemRepository.Item item = ItemRepository
                    .GetAll()
                    .FirstOrDefault(x
                         => x.UrlName.Equals(urlSegment));

                if (item == null)
                {
                    return null;
                }

                segmentContext.RemainingPath = nextSegment.Remaining;

                // Get the child page that can present a full single item
                segmentContext.RoutedContentLink =
                        DataFactory
                          .Instance
                          .GetChildren<ItemPage>(content.PageLink)
                          .First()
                          .PageLink;

                // Put the item's id in segment context
                segmentContext.SetCustomRouteData("itemid", item.Id);

                return null;
            }

            public PartialRouteData GetPartialVirtualPath(ItemPage content,
                string language, RouteValueDictionary routeValues,
                RequestContext requestContext)
            {
                return new PartialRouteData();
            }
        }

        [ModuleDependency(typeof(Web.InitializationModule))]
        public class ItemListPageRouterInitialize : IInitializableModule
        {
            public void Initialize(InitializationEngine context)
            {
                RouteTable.Routes.RegisterPartialRouter(
                    new ItemListPageRouter());
            }

            public void Uninitialize(InitializationEngine context)
            {
            }

            public void Preload(string[] parameters)
            {
            }
        }
    }
}

The item page:

<h1><%: Item.Title %></h1>

<dl>
    <dt>Id</dt>
    <dd><%: Item.Id %></dd>
    <dt>Friendly name</dt>
    <dd><%: Item.UrlName %></dd>
</dl>

The item page code-behind:

namespace EPiServer.Templates.Alloy.Views.Pages
{
    using System.Linq;

    using EPiServer.Templates.Alloy.Business;
    using EPiServer.Templates.Alloy.Models;
    using EPiServer.Templates.Alloy.Models.Pages;
    using EPiServer.Web.Routing;

    public partial class ItemPageTemplate : SiteTemplatePage<ItemPage>
    {
        protected Models.ItemRepository.Item Item { get; set; }

        protected override void OnLoad(System.EventArgs e)
        {
            base.OnLoad(e);
            int itemId = Request.RequestContext.GetCustomRouteData<int>("itemid");
            this.Item = ItemRepository.GetAll().SingleOrDefault(x => x.Id == itemId);

            if (this.Item == null)
            {
                this.Item = new ItemRepository.Item { Title = "No Matching Item Not Found" };
            }
        }
    }
}

The result, notice the URLs:

Screenshot of the resulting pages

Published and tagged with these categories: Episerver, ASP.NET