Per Bjurström
Apr 27, 2009
  11645
(1 votes)

Optimizing Friendly URL for Page Providers

Page Providers are often used to integrate flat external data without a hierarchy. Since everything in EPiServer CMS assumes you have a nice hierarchy you simulate a hierarchy by for example categories or date of creation (i.e. /MyProvider/2009/04/28/My-Data-Item/).

The FriendlyURLRewriteProvider will find the actual page by recursively calling GetChildren and looking at the property PageURLSegment. There are 2 issues you can run into:

1) The URLs are not permanent, you may base your simulated structure on something that will change very often.

2) Performance issues, your backing store is slow or contains large amount of content that you do not want to load into memory just to evaluate a URL (in case you don’t display a tree based on this structure on the site, otherwise this makes no sense since it eventually will be loaded anyway).

The approach taken by me is to:

1) Add an identifier on the PageURLSegment property for your data item so we can use that identifier to resolve pages instead of the friendly URL. This way everything plays nice with EPiServer CMS and the “default” friendly URL would continue to work even without our module.

2) Hook into the URL rewriter and take over those incoming URLs that need to be resolved and use the identifier instead.

The first step is simple, just extend your page provider and add the unique identifier (I am basing this example on the Northwind provider by Allan).

   1: pd.URLSegment += "." + pd.PageLink.ID.ToString();

 

Now we get URLs like “/MyProvider/2009/04/28/My-Data-Item.3434/” and they should work just fine, next step is to hook into the URL rewriter and take over those. I use a HttpModule for that that needs to be registered in web.config.

   1: public void Init(HttpApplication context)
   2:        {
   3:            // This method will be called multiple times for every HttpApplication, since the UrlRewriteModule has one
   4:            // instance per application with non-static events this is perfectly fine.
   5:            UrlRewriteModule m = context.Modules["UrlRewriteModule"] as UrlRewriteModule;
   6:            if(m==null)
   7:                throw new Exception("Cannot find a http module with name UrlRewriteModule");
   8:  
   9:            m.HttpRewritingToInternal += new EventHandler<UrlRewriteEventArgs>(m_HttpRewritingToInternal);
  10:        }

 

The next step is to find “our” URLs so that we can cancel the built-in resolver and make our own lookups:

   1: private void m_HttpRewritingToInternal(object sender, UrlRewriteEventArgs e)
   2:        {
   3:            if (e.Cancel || e.Url==null)
   4:            {
   5:                return;
   6:            }
   7:  
   8:            // Don't do anything when edit-mode passes id=xx since then no parsing is needed
   9:            if (e.Url.QueryCollection["id"] != null)
  10:            {
  11:                return;
  12:            }
  13:  
  14:            // Locking is handled in Init-method
  15:            if(PageReference.IsNullOrEmpty(_entryPoint))
  16:            {
  17:                Init();
  18:            }
  19:  
  20:            // Check if the path follows "our" pattern
  21:            Match match = _urlValidate.Match(e.Url.Path);
  22:            if (!match.Success)
  23:            {
  24:                return;
  25:            }
  26:  
  27:            // Now verify that the path is rooted on our page provider,
  28:            // we do not cache this path since the page could be moved
  29:            string url = GetFriendlyURL(_entryPoint);
  30:            if (e.Url.Path.ToString().StartsWith(url,true,CultureInfo.CurrentCulture))
  31:            {
  32:                PageReference pageLink;
  33:  
  34:                // Try to parse the "id" baked into the URL
  35:                if (PageReference.TryParse(match.Groups["id"].Value, out pageLink))
  36:                {
  37:                    //Get the page from our page provider
  38:                    PageData page = DataFactory.Instance.GetPage(new PageReference(pageLink.ID,PageProviderName));
  39:                    
  40:                    // Rewrite the path and keep any query strings
  41:                    Url urlStaticLink = new Url(page.StaticLinkURL);
  42:                    e.Url.Path = urlStaticLink.Path;
  43:                    e.Url.QueryCollection.Add(urlStaticLink.QueryCollection);
  44:  
  45:                    // Let EPi know we did some changes
  46:                    e.IsModified = true;
  47:                    e.Internal = page.PageLink;
  48:                    e.Cancel = true;
  49:                }
  50:            }
  51:        }

 

TODO

You need to add handling of invalid URLs to this sample. For example if someone changes the URL to “/MyProvider/2009/04/28/I-hate-you.3434/” it would continue to work since we only are looking at the identifier. You need to figure this out by comparing the incoming URL to the actual data and redirect the users to the correct URL instead, I’ve seen sites that do not handle this and its a type of ugly injection that can be misused. Consider yourself warned.

 

Download the demo source

Apr 27, 2009

Comments

Mar 20, 2017 02:17 PM

Apr 10, 2017 12:34 PM

Please login to comment.
Latest blogs
Opti ID overview

Opti ID allows you to log in once and switch between Optimizely products using Okta, Entra ID, or a local account. You can also manage all your use...

K Khan | Jul 26, 2024

Getting Started with Optimizely SaaS using Next.js Starter App - Extend a component - Part 3

This is the final part of our Optimizely SaaS CMS proof-of-concept (POC) blog series. In this post, we'll dive into extending a component within th...

Raghavendra Murthy | Jul 23, 2024 | Syndicated blog

Optimizely Graph – Faceting with Geta Categories

Overview As Optimizely Graph (and Content Cloud SaaS) makes its global debut, it is known that there are going to be some bugs and quirks. One of t...

Eric Markson | Jul 22, 2024 | Syndicated blog

Integration Bynder (DAM) with Optimizely

Bynder is a comprehensive digital asset management (DAM) platform that enables businesses to efficiently manage, store, organize, and share their...

Sanjay Kumar | Jul 22, 2024

Frontend Hosting for SaaS CMS Solutions

Introduction Now that CMS SaaS Core has gone into general availability, it is a good time to start discussing where to host the head. SaaS Core is...

Minesh Shah (Netcel) | Jul 20, 2024

Optimizely London Dev Meetup 11th July 2024

On 11th July 2024 in London Niteco and Netcel along with Optimizely ran the London Developer meetup. There was an great agenda of talks that we put...

Scott Reed | Jul 19, 2024