Over the past few days, I’ve been trying to come up with a good idea for a blog post showing the usage of the Decorator design pattern in Sitecore.
During this time of cogitation, I was having difficulties coming up with a good example despite having had used this pattern in Sitecore on many past projects — I can’t really share those solutions since they are owned by either previous employers or clients.
However, I finally had an “EUREKA!” moment after John West — CTO of Sitecore USA — wrote a blog post earlier today where he shared an <httpRequestBegin> pipeline processor which redirects to a canonical URL for an Item.
So, what exactly did I come up with?
I built the following example which simply “decorates” the “out of the box” ItemResolver — Sitecore.Pipelines.HttpRequest.ItemResolver in Sitecore.Kernel.dll — which is used as an <httpRequestBegin> pipeline processor to figure out what the context Item should be from the URL being requested by looking for an entry in the IDTable in Sitecore (note: this idea is adapted from a blog post that Alex Shyba — Director of Platform Innovation and Engineering at Sitecore — wrote a few years ago):
using Sitecore; using Sitecore.Data; using Sitecore.Data.IDTables; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.Pipelines.HttpRequest; namespace Sitecore.Sandbox.Pipelines.HttpRequest { public class IDTableItemResolver : HttpRequestProcessor { private string Prefix { get; set; } private HttpRequestProcessor InnerProcessor { get; set; } public override void Process(HttpRequestArgs args) { Assert.ArgumentNotNull(args, "args"); AssertProperties(); Item item = GetItem(args.Url.FilePath); if (item == null) { InnerProcessor.Process(args); return; } Context.Item = item; } protected virtual void AssertProperties() { Assert.IsNotNullOrEmpty(Prefix, "Prefix", "Prefix must be set in configuration!"); Assert.IsNotNull(InnerProcessor, "InnerProcessor", "InnerProcessor must be set in configuration!"); } protected virtual Item GetItem(string url) { IDTableEntry entry = IDTable.GetID(Prefix, url); if (entry == null || entry.ID.IsNull) { return null; } return GetItem(entry.ID); } protected Item GetItem(ID id) { Database database = GetDatabase(); if (database == null) { return null; } return database.GetItem(id); } protected virtual Database GetDatabase() { return Context.Database; } } }
What is the above class doing? It’s basically seeing if it can find an Item for the passed relative URL — this is passed via the FilePath property of the Url property of the HttpRequestArgs instance taken in by the Process() method — by delegating to a method that looks up an entry in the IDTable for the URL — the URL would be the key into the IDTable — and return the Item from the context database if an entry is found. If no entry is found, it just returns null.
If null is returned, that pretty much means there is no entry in the IDTable for the given relative URL so a delegation to the Process() method of the InnerProcessor is needed in order to preserve “out of the box” Sitecore functionality for Item URL resolution.
I then replaced the “out of the box” ItemResolver with the above in the following patch include configuration file:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <httpRequestBegin> <processor patch:instead="*[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" type="Sitecore.Sandbox.Pipelines.HttpRequest.IDTableItemResolver, Sitecore.Sandbox"> <Prefix>UrlRewrite</Prefix> <InnerProcessor type="Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel" /> </processor> </httpRequestBegin> </pipelines> </sitecore> </configuration>
In the above configuration file, we are setting the “out of the box” ItemResolver to be injected into the class above so that its Process() method can be “decorated”.
Let’s see this in action!
Let’s try this out with the following page Item:
In order to see the above <httpRequestBegin> pipeline processor in action, I had to add an entry into my IDTable — let’s make pretend an UrlRewrite module on the Sitecore Marketplace added this entry for us:
I loaded up another browser window; navigated to the relative URL specified in the IDTable entry; and then saw the following:
As you can see, it worked.
We can also navigate to the same page using its true URL — the one resolved by Sitecore “out of the box”:
The above worked because the inner processor resolved it.
Let’s now go to a completely different page Item altogether. Let’s use this one:
As you can see, that also worked:
If you have any thoughts on this, or have other ideas around using the Decorator pattern in Sitecore, please share in a comment.
Excellent post Mike. And I like the idea of writing about patterns in specific relation to their application to Sitecore problems. Keep them coming.
Thanks Martin!
There are more posts to come.
Mike
[…] Decorator Pattern: One of the most commonly used design pattern that is served as a wrapper to your objects within the same interface and hence you can apply changes to this single object without affecting the behavior of other objects in the same place. You can find more detailed implementation here. […]
[…] that we have code that can find a bucketed Item by name under an Item Bucket, we need a custom Item Resolver — this is just a custom <httpRequestBegin> pipeline processor — that uses an […]