Customising URLs in Umbraco

By default, Umbraco builds logical URLs based on the backoffice content structure - usually this works fine, but there are cases when it’s useful to be a bit clever.

I’m currently working on adding the fantastic Articulate blogging package to a site, and was not entirely satisfied with the URL structure.

Articulate creates URLs along these lines - /blog/archive/post-slug - where /archive/ can be renamed in the backoffice. What I wanted instead was to remove it all together.

Enter Umbraco’s IUrlProvider and IContentFinder.

I won’t pretend to have a deep understanding of Umbraco’s request pipeline and how these two gems fit into it, but they essentially let us modify URLs however we see fit, and ensure that teh correct content is returned for the requested, modified URL.

All that’s happening in here is for content nodes representing Articulate blog posts, I’m modifying the returned url to remove the node’s immediate parent. Happy days.

In the IContentFinder, we need to ensure a request to a modified URL still returns the correct content. All we have to work with is the URL name of the requested node, and a bit of insight into what doctype to expect at a modified URL.

It’s as simple as this (although it can be made more complex by caching the returned node to prevent subsequent lookups):

publicclassArticulateContentFinder:IContentFinder{publicboolTryFindContent(PublishedContentRequestcontentRequest){try{if(contentRequest!=null){varpath=contentRequest.Uri.GetAbsolutePathDecoded();varparts=path.Split(new[]{'/'},System.StringSplitOptions.RemoveEmptyEntries);if(parts.IndexOf("blog")!=-1){IEnumerable<IPublishedContent>blogItems=UmbracoContext.Current.ContentCache.GetByXPath("//ArticulateRichText");varblogItem=blogItems.Where(x=>x.UrlName==parts.Last()).FirstOrDefault();if(blogItem!=null){contentRequest.PublishedContent=blogItem;}}}}catch(Exceptionex){// do something with the exception.}returncontentRequest.PublishedContent!=null;}}

Since we know we’ve modified blog posts, we can concern ourselves only with nodes with ‘blog’ in the url. From there, it’s just a matter of fetching the nodes of the correct type, checking that the UrlName property matches the requested path, and updating the contentRequest if appropriate.

There’s no need to worry about setting the template for Articulate nodes, as the package (apparently) does this for us.

Once these two are sorted, all that’s left is to register them on startup: