---
title: Example of News partial routing
slug: cms-11/dev-docs/example-of-news-partial-routing
docTags: 
createdAt: 2026-07-22T19:45:18.655Z
---

In the example, the part of the URL that is `http://site/News/` is the URL to a page instance of model type `NewsContainer`. By registering a partial router for `ModelType NewsContainer`, the partial router routes the remaining part of the URL. The partial router handles routing `Sports/TheGame/`.

## Extend routing for the NewsContent type

The following example extends routing for the `NewsContainer` content type:

```csharp
[ContentType]
public class NewsContainer: PageData {}

Next, implement a NewsContent type class:

public enum NewsCategory {
  Sports,
  News,
  Economy
}
public class NewsContent {
  public virtual NewsCategory Category {
    get;
    set;
  }
  public virtual string Name {
    get;
    set;
  }
  public virtual string Body {
    get;
    set;
  }
}
```

This code example is just one way of implementing a partial router. It uses DDS as external storage with no caching and is not meant as production-ready code.

## Implement NewsContentStore

Create a simple store to deliver `NewsContent` instances because `NewsContent` is stored outside Optimizely Content Management System (CMS) (in Optimizely Dynamic Data Store).

You also could return the routed data as `IContent` instances, where you can deliver the data through a custom content provider. Or, you can store the data in CMS so that no provider is needed and the data can be loaded or saved through `EPiServer.IContentRepository`.

In the following example, partially route URL parts like `Sports/TheGame/` by adding a method to the store that accepts a category (that corresponds to `Sports` in the URL example) and the name of an article (that corresponds to `TheGame` in the URL example).

```csharp
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(NewsCategory category, string name) {
    return News.Where(n => n.Category == category &&
        n.Name == name)
      .SingleOrDefault();
  }
}
```

## Implement IPartialRouter

Make the partial router handle incoming requests beyond pages of type `NewsContainer`. Also, lets you create outgoing FURLs for instances of `NewsContent`.

```csharp
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);

    NewsCategory category;
    if (Enum.TryParse < NewsCategory > (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

}
```

## Register a router

The following example initializes code that registers the partial router.

```csharp
[ModuleDependency(typeof (EPiServer.Web.InitializationModule))]
public class InitializationModule: IInitializableModule {
  public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context) {
    //It checks for a page of type NewsContainer named News under start page. If it does not exist it gets created.
    //Then partial routing is extended beyond that page.
    var newsPage = context.Locate.ContentLoader().GetChildren < NewsContainer > (ContentReference.StartPage).GetFirstOrDefault();
    if (newsPage == null) {
      newsPage = context.Locate.ContentRepository().GetDefault < NewsContainer > (ContentReference.StartPage);
      newsPage.Name = "News";
      context.Locate.ContentRepository().Save(newsPage, SaveAction.Publish, AccessLevel.NoAccess);

    }

    var partialRouter = new NewsPartialRouter(new NewsContentStore(), newsPage.ContentLink.ToReferenceWithoutVersion());
    RouteTable.Routes.RegisterPartialRouter < NewsContainer, NewsContent > (partialRouter);

    NewsContentGenerator.CreateFakeData();
  }

  public void Preload(string[] parameters) {}

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

## Register MVC controllers

In CMS, you register an MVC controller for a Model type by implementing the `EPiServer.Web.IRenderTemplate<TModel>` interface, which occurs implicitly if your controller inherits `EPiServer.Web.Mvc.PageController<TPage>` or `EPiServer.Web.Mvc.BlockController<TBlock>`. The following code is for an MVC controller for `NewsContent` and `NewsContainer` that uses an extension method in the `EPiServer.Web.Routing` namespace.

```csharp
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);
  }
}

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());
  }
}
```

## Create MVC views

To display a single news from the above controller, create a view located as `/Views/NewsContent/index.aspx`. Also, create a view for the `NewsContainer` class that lists news with partially routed FURLs.

```csharp
<%@ Page Title="" Language="C#"
    Inherits="System.Web.Mvc.ViewPage<CodeSamples.Additional_Content.HowTo.PartialRouting.NewsContent>" %>
    
    <h2><%=Model.Name%></h2>
    <p><%=Model.Body %></p>

    <%@ 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="<%=UrlResolver.Current.GetVirtualPathForNonContent(news, null, null).GetUrl()%>"><%=news.Name%></a>
        </li>
    <%}%>
    </ul>
```

## Create outgoing URLs

To create an outgoing URL for a `NewsContent` item, call the `EPiServer.Web.UrlResolver.Current.GetVirtualPathForNonContent` method. For `IContent` instances, you also can call the `GetVirtualPath` or `GetUrl` methods and pass in the reference to the content. In web forms, you can create a link in the HTML, such as `/Templates/NewsContent.aspx?id=7__news`, then the FURL module calls the partial router during outgoing parsing to create an FURL for the content. The following example shows how to construct outgoing FURLs.

```csharp
NewsContent news = GetNewsContent();
var furl = urlResolver.GetVirtualPathForNonContent(news, null, 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();
```

## Create NewsContent

The following example shows how to route to data stored outside CMS. Use `DynamicDataStore` to store `NewsContent`. The following code creates `NewsContent` instances and stores them in `DynamicDataStore`.

```csharp
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 = NewsCategory.Sports,
          Name = "Sweden",
          Body = "Sweden have qualified for EURO 2012 championship in soccer"
      };
      newsContentStore.Save(soccerNews);

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

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

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

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

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