Try our conversational search powered by Generative AI!

Loading...
Area: Optimizely CMS
ARCHIVED This content is retired and no longer maintained. See the latest version here.

Recommended reading 

Table of Contents

Introduction

This example will implement partial routing for URLs like http://site/News/Sports/TheGame/. In the example the part of the url that is http://site/News/ is the url to a page instance of model type NewsContainer. Then by registering a partial router for ModelType NewsContainer the partial router takes care of routing the remaining part of the url. In the example the partial router will take care of routing the part 'Sports/TheGame/'.

The NewsContent Type

We want to extend routing for content type NewsContainer which look like.

C#
[ContentType]
public class NewsContainer : PageData
{ }

Then we implement a type NewsContent. The class looks like:

C#
public enum NewsCateory
{
    Sports,
    News, 
    Economy
}
public class NewsContent
{
    public virtual NewsCateory Category { get; set; }
    public virtual string Name { get; set; }
    public virtual string Body { get; set; }
}

Implementing NewsContentStore

This is an example where NewsContent is stored outside EPiServer CMS (in Dynamic Data Store) therefore we will write a simple store that deliver NewsContent instances. Another option would be to return the routed data as IContent instances. In that case the data could either be delivered through a custom content provider (see section Content Providers) or the data can be stored in EPiServer CMS. Then no provider would be needed instead the data could be loaded/saved through EPiServer.IContentRepository (see section Persisting IContent instances).

In this example we want to partial route URL parts like 'Sports/TheGame/' so we add a method to our store that accepts a category (corrsponds to 'Sports' in URL example) and the name of an article (corresponds to 'TheGame' in URL example). The store implementation look like:

C#
public class NewsContentStore
{
    //In this example the data is stored in DDS
    public IOrderedQueryable<NewsContent> News { get { return DynamicDataStoreFactory.Instance.
        GetStore(typeof(NewsContent)).Items<NewsContent>(); } }

    public NewsContent RouteContent(NewsCateory category, string name)
    {
        return News.Where(n => n.Category == category
            && n.Name == name)
            .SingleOrDefault();
    }
}

Implementing IPartialRouter

We want the partial router to be able to handle incoming request beyond pages of type NewsContainer. We also want to be able to create outgoing FURLs for instances of NewsContent. The implementation of the partial router looks like:

C#
public class NewsPartialRouter : IPartialRouter<NewsContainer, NewsContent>
{
    private NewsContentStore _newsStore;
    private ContentReference _newsContainer;

    public NewsPartialRouter(NewsContentStore newsStore, ContentReference newsContainer)
    {
        _newsStore = newsStore;
        _newsContainer = newsContainer;
    }

    #region RoutePartial
    public object RoutePartial(NewsContainer content, SegmentContext segmentContext)
    {
        //The format we handle is category/Name/
        NewsContent newsContent = null;

        //Use helper method GetNextValue to get the next part from the URL
        var nextSegment = segmentContext.GetNextValue(segmentContext.RemainingPath);

        NewsCateory category;
        if (Enum.TryParse<NewsCateory>(nextSegment.Next, out category))
        {
            nextSegment = segmentContext.GetNextValue(nextSegment.Remaining);
            if (!String.IsNullOrEmpty(nextSegment.Next))
            {
                newsContent = _newsStore.RouteContent(category, HttpUtility.UrlDecode(nextSegment.Next));
                if (newsContent != null)
                {
                    //Update RemainingPath so the part that we have handled is removed.
                    segmentContext.RemainingPath = nextSegment.Remaining;
                }
            }
        }

        return newsContent;
    }
    #endregion

    #region GetPartialVirtualPath
    public PartialRouteData GetPartialVirtualPath(NewsContent content, string language, RouteValueDictionary routeValues, RequestContext requestContext)
    {
        if (ContentReference.IsNullOrEmpty(_newsContainer))
        {
            throw new InvalidOperationException("property NewsContainer must be set on start page");
        }
        return new PartialRouteData()
        {
            BasePathRoot = _newsContainer,
            PartialVirtualPath = String.Format("{0}/{1}/",
                content.Category.ToString(),
                HttpUtility.UrlPathEncode(content.Name))
        };
    }
    #endregion

}

Registering a Router

Below is the code initialization code that registers the partial router.

C#
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class InitializationModule : IInitializableModule
{
    public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context)
    {
        var startPage = context.Locate.ContentLoader().Get<PageData>(PageReference.StartPage);

        //There should be a property on start page that sets the newsContainer page.
        var partialRouter = new NewsPartialRouter(new NewsContentStore(), startPage["NewsContainer"] as ContentReference);
        RouteTable.Routes.RegisterPartialRouter<NewsContainer, NewsContent>(partialRouter);

        NewsContentGenerator.CreateFakeData();
    }

    public void Preload(string[] parameters)
    {
    }

    public void Uninitialize(EPiServer.Framework.Initialization.InitializationEngine context)
    {
    }
}

Registering MVC Controllers

In EPiServer CMS you register a MVC controller or WebForm for a Model type by implementing interface EPiServer.Web.IRenderTemplate<TModel>. This is done implicitly if your controller inherits EPiServer.Web.Mvc.PageController<TPage> or EPiServer.Web.Mvc.BlockController<TBlock> or if your WebForm inherits EPiServer.PageBase<TPage>. Below is the code for a Mvc controller for NewsContent and NewsContainer.

C#
public class NewsContentController : System.Web.Mvc.Controller, IRenderTemplate<NewsContent>
{
    public ActionResult Index()
    {
        //You get the routed custom data from extension method in EPiServer.Web.Routing
        var newsContent = Request.RequestContext.GetRoutedData<NewsContent>();
        return View(newsContent);
    }
}
C#
public class NewsContainerController : PageController<NewsContainer>
{
    public ActionResult Index()
    {
        //The view for news container displays a list of all news.
        var newsStore = new NewsContentStore();
        return View(newsStore.News.ToList());
    }
}

Creating MVC Views

To display a single news from the above controller we create a view located as /Views/NewsContent/index.aspx. We also create a view for the NewsContainer class that lists all news with partial routed FURLs. The below code is the code for the views.

C#
<%@ Page Title="" Language="C#"
Inherits="System.Web.Mvc.ViewPage<CodeSamples.Additional_Content.HowTo.PartialRouting.NewsContent>" %>

<h2><%=Model.Name%></h2>
<p><%=Model.Body %></p>
C#
<%@ Page Title="" Language="C#" 
Inherits="System.Web.Mvc.ViewPage<List<CodeSamples.Additional_Content.HowTo.PartialRouting.NewsContent>>" %>
<%@ Import Namespace="System.Web.Routing" %>
<%@ Import Namespace="EPiServer.Web.Routing" %>
<h2>List of news</h2>
<ul>
<%foreach (var news in Model) {%>
    <li>
        <a href="<%=RouteTable.Routes.GetVirtualPathForPartialRouted(news, null).GetUrl()%>"><%=news.Name%></a>
    </li>
<%}%>
</ul>

Creating Outgoing URLs

To create an outgoing url for a NewsContent item you can call the extension method GetVirtualPathForPartialRouted<T>(object routedData, string language) on RouteCollection (the extension method is located in namespace EPiServer.Web.Routing). For IContent instances it is also possible to call method GetVirtualPath(ContentReference contentLink, string language) or in web forms create a link in the html like '/Templates/NewsContent.aspx?id=7__news' then the FURL module will call the partial router during outgoing parsing to create an FURL for the content. Below is an example of how outgoing FURLs can be constructed.

C#
NewsContent news = GetNewsContent();
var furl = RouteTable.Routes.GetVirtualPathForPartialRouted(news, null).GetUrl();

//In case the item is an IContent then in webforms you can also output a link like:
IContent iContent = GetIContent();
var anchor = new System.Web.UI.HtmlControls.HtmlAnchor();
anchor.HRef = "/templates/NewsContent.aspx?id=" + iContent.ContentLink.ToString();

Creating NewsContent

In this example we show how data stored outside EPiServer CMS can be routed to. In this example we use DynamicDataStore to store our NewsContent. The follow code creates some NewsContent instance and store them in DynamicDataStore.

C#
public class NewsContentGenerator
{
    public static void CreateFakeData()
    {
        var newsContentStore = DynamicDataStoreFactory.Instance.GetStore(typeof(NewsContent));
        if (newsContentStore == null)
        {
            newsContentStore = DynamicDataStoreFactory.Instance.CreateStore(typeof(NewsContent));

            var soccerNews = new NewsContent()
            {
                Category = NewsCateory.Sports,
                Name = "Sweden",
                Body = "Sweden have qualified for EURO 2012 championship in soccer"
            };
            newsContentStore.Save(soccerNews);

            var olymicNews = new NewsContent()
            {
                Category = NewsCateory.Sports,
                Name = "Olympic",
                Body = "The next summer olympic will take place in London"
            };
            newsContentStore.Save(olymicNews);

            var euroNews = new NewsContent()
            {
                Category = NewsCateory.Economy,
                Name = "Euro",
                Body = "The euro has reached new levels"
            };
            newsContentStore.Save(euroNews);

            var oilNews = new NewsContent()
            {
                Category = NewsCateory.Economy,
                Name = "Oil",
                Body = "New oil findings have been made in the artic"
            };
            newsContentStore.Save(oilNews);

            var politicNews = new NewsContent()
            {
                Category = NewsCateory.News,
                Name = "Selection",
                Body = "Sweden have selected a new government"
            };
            newsContentStore.Save(politicNews);

            var earthQuakeNews = new NewsContent()
            {
                Category = NewsCateory.News,
                Name = "Eartquake",
                Body = "An earthquake was registered this morning"
            };
            newsContentStore.Save(earthQuakeNews);
        }
    }
}

See Also

  • In the Relate template package you can see an implementation of partial routing for blogs and blog entries.
  • In the section Content Providers you will find more information on how to create content providers.
  • In the SDK framework reference you will find more information on the EPiServer.Web.Routing.IPartialRouter<TIncoming, TOutgoing> and base class.
Do you find this information helpful? Please log in to provide feedback.

Last updated: Mar 25, 2013

Recommended reading