Building extension-less Urls in Sitecore

by aboo bolaky 4. October 2009 08:55

Our goal here is to build a Sitecore solution having links without the .aspx extension.(although accessing a page with a .aspx extension should STILL work)

To start ,you need

  • A LOT OF PATIENCE
  • Helicon ISAPI Rewrite Lite (the free one). Since I'm usingWindows 7 RC1 x64bit, I'll need to download the x64 bit flavour of Helicon Lite
  • A test Sitecore Application .. I will be using the Sitecore Starter Kit [Sitecore Starter Kit 6.0.0 rev.090203] as an my starting point.  (installed on IIS 7).

Before I start on implementing the solution, a little bit of background info would, I guess, be quite useful.

AddAspxExtension in LinkManager

A potential solution is to change the value of AddAspxExtension from true (by default) to false. If you do change it to false, you will have to create a wildcard script map to the ASP.NET runtime. This causes IIS to intercept every request made against the web server. This includes requests for images, asp.net pages and HTML pages. Therefore, enabling a wildcard script map to ASP.NET does have performance implications. If you wish to find another way to use pages without .aspx extensions in Sitecore, please read further....

Sitecore Aliases

Aliases, in a nutshell, allow you to shorten the url of an item. For example, if your item is currently accessible via http://hostname/MyParentItem/MyChildItem.aspx, you can specify an alias of myChildItem, which will be a placeholder for the actual item as it is in the Sitecore tree. Hence, the url of the alias is http://hostname/MyChildItem.aspx. For SEO purposes, this allows us to surface items from deep down in the hierarchy right up to the site root.

Note:

  • Aliases do not work if you remove the .aspx extension
  • No matter how far your items are in the sitecore tree, an alias allows you to refer to it from the site root.

Step 1: Install and Configure Helicon ISAPI Rewrite Lite

Start by installing Helicon ISAPI Rewrite. This process is fairly straightforward. Since we are using the lite version,  our Regex entries will be placed in the global http.conf (located in the Lite version installation folder). The entries in my httpd.conf are as follow:-

RewriteEngine on
RewriteBase /
RewriteRule ^(sitecore.*)$ $1 [L]
RewriteRule ^([^\.\?]+)/?(\?.*)?$ /$1.aspx$2 [L]

 

Url Rewriting Rationale

Before a request is forwarded to Sitecore, the ISAPI module intercepts it.

Line 1: You NEED that ! This turns on the Helicon ISAPI Module

Line 2: Errr...This is self-explanatory..

Line 3: We don't need to chop off the .aspx when we are in Sitecore CMS. For this reason, we're basically telling the module to not do anything when a request has "sitecore" in it.

Line 4: This is the most important bit. This appends .aspx (and querystrings,if any) to requests and consequently forwards the resulting request to Sitecore. Two scenarios arise as a result of this.

 3.1 : Sitecore maps the request to an item in the database. The page gets displayed in the end.

 3.2 : Sitecore cannot find the item based on the url. You will either end up with the  Sitecore's "Item Not Found" page.

NB:

Before we go any further, I need to confess that I did modify the Starter Kit a little prior to this operation. Basically, when you load the starter kit, you are greeted with a dummy home Site, that has a nice layout and there is a link to the Actual Starter Site. I was tired of this as my home page, So, I changed the value of "startItem" [in the Sites Definition of website (in web.config)] from "/home" to "/Sample". In that way, when i hit the website, I will eventually land on the real starter site!. Also, by doing so, all my urls within the website will no longer contain "/sitecore/content/.." since the Start Item has changed.

Quick Test on Urls

Request : http://sitecorestarterkit/References.aspx  [OK]

Request : http://sitecorestarterkit/References  [OK]

Request : http://sitecorestarterkit/Services/Architectual-Services.aspx [OK]

Request : http://sitecorestarterkit/Services/Architectual-Services [OK]

Request : http://sitecorestarterkit/Sitecore/  [OK..CMS access]

It looks like we have a half-baked solution. Aliases will now work without the .aspx extension as well. The other bits that need to be considered are

1 : How to make sitecore controls (.e.g. sc:link etc..) aware that they should drop the ".aspx" extensions

2 : How does it all tie up together with .NET (user controls etc..)

Step 2: XSL Extensions (revised)

To follow up on custom solution, you will need to tell Sitecore to remove the ".aspx" when it renders urls (either via sc:link [xsl extensions] or c# code). For XSL Extensions, we need to disable the default implementation that Sitecore provides us with and roll out our own. Fortunately, it's very easy to do so. [Credits : Chris Wojciech ]

 

2.1 : Turn off the default XslHelper

<xslExtensions>
<!-- Changed from "On" to "Off" -->
<extension mode="off" type="Sitecore.Xml.Xsl.XslHelper, Sitecore.Kernel" 
	namespace="http://www.sitecore.net/sc" singleInstance="true" />
	......
</xslExtensions>

  

  

2.2 : Create your own XslHelper

 namespace Starterkit.Utils
 {
	 public class XslHelper : Sitecore.Xml.Xsl.XslHelper
	{
		public override string path(System.Xml.XPath.XPathNodeIterator iterator)
		{
			string path = base.path(iterator);
			string newPath = Regex.Replace(path, ".aspx", String.Empty, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			return newPath;
		}
		public override string link(string fieldName, System.Xml.XPath.XPathNodeIterator iterator, string parameters)
		{
			string path = base.link(fieldName, iterator, parameters);
			string newPath = Regex.Replace(path, ".aspx", String.Empty, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			return newPath;
		}
		public override string StartLink(System.Xml.XPath.XPathNodeIterator iterator, string parameters)
		{
			string path = base.StartLink(iterator, parameters);
			string newPath = Regex.Replace(path, ".aspx", String.Empty, RegexOptions.IgnoreCase | RegexOptions.Compiled);
			return newPath;
		}
 
	}
}

 

For each of those three methods, we're only replacing the .aspx with an empty string. To enable <sc:link/> to use our custom Xsl Helper, we need to add another entry to the <xslextensions> section

 

<xslExtensions>
	<!-- Changed from "On" to "Off" -->   
	<extension mode="off" type="Sitecore.Xml.Xsl.XslHelper, Sitecore.Kernel" 
	namespace="http://www.sitecore.net/sc" singleInstance="true" />  
	<extension mode="on" type="Starterkit.Utils.XslHelper, Starterkit.Utils"
	 namespace="http://www.sitecore.net/sc" singleInstance="true" />
	 ....... 
 </xslExtensions>
 

 

NEARLY THERE!!!. All the links (that are rendering using sc:link) have now lost the .aspx extensions on the front end.

Step 3 : Sitecore and .NET interaction (with Url Rewriting)

If you have a Sitecore solution built using XSLT renderings only (highly unlikely though..), you're kinda safe here. However, if you have usercontrols (that host controls that can cause a postback) as well (for argument's sake, a contact us form), you end up with one issue.

Let's create a Contact Us form and add it to the presentation of the Contact Us item in Sitecore

 

User Control Designer

 <%@ Control Language="c#" AutoEventWireup="true"
 TargetSchema="http://schemas.microsoft.com/intellisense/ie5" 
 Inherits="Layouts.Contactus.ContactusSublayout"
 Codebehind="~/layouts/Starter Kit/Sublayouts/ContactUs.ascx.cs" %>
 
 <asp:Label Text="First Name : "  AssociatedControlID="txtFirstName" runat="server" />
 <asp:TextBox ID="txtFirstName" runat="server" />
 <asp:Label Text="First Name : "  AssociatedControlID="txtFirstName" runat="server" /> 
 <asp:TextBox ID="TextBox1" runat="server" />
 <asp:Button ID="btnSend" Text="Send" runat="server" />
  
   

 

Page Source

 <form name="mainform" method="post"
 action="/Contact.aspx" id="mainform">
 


To solve this, you will need to create a Control Adapter for the Forms in your application. Control Adapters allow you to inject custom code within the rendering of a control.

 

Form Control Adapter

namespace Starterkit.Utils
{
	public class FormActionRewriter : System.Web.UI.Adapters.ControlAdapter
	{
		protected override void Render(System.Web.UI.HtmlTextWriter writer)
		{
			base.Render(new RewriteFormHtmlTextWriter(writer));
		}
  	}
	class RewriteFormHtmlTextWriter : HtmlTextWriter
	{
		public RewriteFormHtmlTextWriter(HtmlTextWriter writer)
			: base(writer)
		{
			this.InnerWriter = writer.InnerWriter;
		}
 		public RewriteFormHtmlTextWriter(System.IO.TextWriter writer)
			: base(writer)
		{
			base.InnerWriter = writer;
		}
		public override void WriteAttribute(string name, string value, bool fEncode)
		{
 			if ((name == "action"))
			{
				HttpContext Context = null;
				Context = HttpContext.Current;
 
				if (Context.Items["ActionAlreadyWritten"] == null)
				{
					if ((!Context.Request.RawUrl.Contains("sitecore")))
					{   //remove .aspx extension if we're on the front end
						value = Regex.Replace(Context.Request.RawUrl, ".aspx", String.Empty, RegexOptions.IgnoreCase | RegexOptions.Compiled);
						Context.Items["ActionAlreadyWritten"] = true;
					}
				}
 			}
			base.WriteAttribute(name, value, fEncode);
		}
	}
}

Add Form Control Adapter in Solution

Open the form.browser (located in ~/App_Browsers) and add the new entry

<browsers> 
	<browser refID="Default">
		<controlAdapters>    
			<adapter controlType="System.Web.UI.HtmlControls.HtmlForm" 
			adapterType="Sitecore.Web.FormAdapter, Sitecore.Kernel" />  
			<!--Added--> 
			<adapter controlType="System.Web.UI.HtmlControls.HtmlForm"
			adapterType="Starterkit.Utils.FormActionRewriter, Starterkit.Utils" />    
		</controlAdapters> 
	</browser>
</browsers>

THIS IS IT!!!

<form name="mainform" method="post"
 action="/Contact" id="mainform">

 

RESULT !!! .... Back to Sitecore :)

Tags: ,

.Net | Applications | Sitecore

Comments

5/10/2010 5:20:53 PM #

led light

Thank you for sharing with us,thanks!

led light United States |

5/14/2010 10:23:11 AM #

vanillin

Praise to this blog because I have been using too many extensions and this is the solution I really need. I have bookmarked this page and thank you again. (^_^)

vanillin United States |

5/17/2010 5:16:43 PM #

led light

Your post is easy to understand. Thanks for sharing

led light People's Republic of China |

5/24/2010 11:41:16 AM #

cctv-cameras

This looks absolutely perfect. All these tinny details are made with lot of background knowledge. I like it
a lot.

cctv-cameras Canada |

5/24/2010 11:41:47 AM #

cctv ccd camera

Thank you for sharing with us,I too always learn something new from your post!

cctv ccd camera Canada |

5/24/2010 11:42:44 AM #

cctv ccd camera

Thanks a lot for enjoying this beauty article with me.

cctv ccd camera Canada |

11/3/2011 1:58:48 PM #

the new iphone

Whoah this weblog is fantastic i love studying your posts. Keep up the good paintings! You already know, many people are looking round for this information, you can help them greatly.

the new iphone United States |

11/5/2011 7:01:23 AM #

Boy Games


Enjoy our free Boy Games and free games

Boy Games United States |

11/7/2011 10:58:20 AM #

visite Marrakech

I think that is with the most expressive info for me. Further i’m cheerful reading your essay. Still wanna comment on part common objects, The website fashion is incredible, the essays is in thesis of phenomenon lovely Souriant . Even option assignment, brightens.

visite Marrakech France |

11/7/2011 1:50:19 PM #

Marrakech ici

I gotta special this locale it appears same cooperative same desirable

Marrakech ici France |

11/13/2011 9:58:24 AM #

voir Marrakech

As I website proprietor I create the meaning here is actual brilliant , remarks for your labors.

voir Marrakech France |

11/13/2011 12:59:56 PM #

sejour Marrakech

As soon as I noticed this website I went on reddit to lot some of the love with them. “A sect or party is an courtly concealed devised to skimp a hombre from the irritation of thinking.” by Ralph Waldo Emerson.

sejour Marrakech France |

11/15/2011 10:38:00 PM #

annonces immobilieres maroc

I loved as very as you volition have carried absent perquisite here. The delineate is savory, your authored palpable sharp. still, you mastery gain got an edginess through that you crave be turning in the audience. evil indubitably befall plus in the prior former plus as quite the equal almost quite continuously internal case you protect this backpack.

annonces immobilieres maroc France |

11/16/2011 4:04:30 AM #

credit immobilier au maroc

Wohh precisely what I was looking for, views for putting up. “The lone course of aware a soul is to passion them minus desire.” by Walter Benjamin.

credit immobilier au maroc France |

Tag cloud

Flash Player 9 required.

About Me

I wish I could write something here..
//TODO: ElaborateMe