Monday, September 12, 2011

Fixing Sitefinity 3.7 URL handling

One of the things I’ve been wrestling with lately is Sitefinity’s somewhat outdated way of handling URL’s. There are two issues that can be fixed relatively easily: PathInfo handling and support for IIS URL rewriting module.

UPDATE: There’s a follow up to this article with more fixes.

Sitefinity seems to have been built with a notion that all URL’s look like this:

A direct url to a page and optionally a query string (the part after ?).
However, nowadays we like pretty url’s, and for good reasons.
A prettier version of the above url could look like this:

It’s even better to get rid of the .aspx file extension, but that’s beyond the scope of this post. The bit of the URL after .aspx is known in ASP.NET as PathInfo.

If you try to implement this type of URL in Sitefinity you’ll run into some oddity in the way Sitefinity parses URL’s.

Sitefinity scans the URL to find the page name by looking for the last period (.) in the path (excluding the query string). In the first URL, that’ll work. In the second URL, it won’t. It’ll give a 404 because Sitefinity thinks you’re not requesting a resource managed by the CMS.

The remedy is to hand Sitefinity only the path to the page and the query string and strip out the additional path information. No actual information is lost since the ASP.NET Request object will still hold the original URL, including the path info. We’re just not telling Sitefinity about it…

URL rewriting
Another issue I have with Sitefinity is in the way URL rewriting is handled. Sitefinity offers it’s own Advanced URL rewriting; which basically consists of regex replacements. It works just fine. However there’s also an IIS Rewrite Module that does the same thing (and a lot more) and it’s fully integrated with IIS7 admin console for easy management.

Upon rewriting a URL the IIS Rewrite Module modifies the HttpRequest.Url property.
Sitefinity however does not get that modified URL because it works with HttpRequest.RawUrl ( also reported by Joe Groner here) which contains the URL requested by the browser.

The fact that these properties differ also enables us to detect whether a URL has been rewritten. The fix is to return the rewritten URL in the same way that Sitefinity’s own rewriting mechanism would.

Update: There is one small detail that should not be overlooked. The Uri.PathAndQuery property returns an escaped path where HttpRequest.RawUrl is an unescaped. This means the path returned by Uri.PathAndQuery needs to be unescaped otherwise encoded characters in the path (like %20 for space) will not be handled correctly. In Integrated Pipeline mode it could even mean that requests for static files will fail with 404.0 – Not found.

Both fixes must be applied to the HttpModule used by Sitefinty as it’s main entry point: CmsHttpModule. To allow Sitefinity’s own rewriting logic to continue to work I’ve subclassed Telerik.Cms.Web.CmsHttpModuleUrlRewrite (classic ASP.NET pipeline) and Telerik.Cms.Web.CmsHttpModuleIIS7Integrated (integrated pipeline).

Full source on github:
The required changes to web.config are also listed there.

UPDATE: Please see the follow up post for more information and a link to the GitHub repository.

Please note that this solution is not intended for sites that use extensionless URL’s.