Moving to extensionless URLs. Suggestions?

Topics: General, Standard packages, Troubleshooting
Aug 9, 2011 at 5:30 PM

I'm looking for suggestions on the best way to move to extensionless URLs. We've upgraded to the latest version of Composite C1 which has URL Routing baked in and would like to use it. ==>

My thoughts are that we can simply use the friendly URL.

However, the tricky part is we're also planning on switching from Query Strings to PathInfo.

We'll be switching: ==>

Ideally, I'd like to redirect the query string values to the pathinfo version, but I'm not sure the best way to do this in Composite.'

Any ideas?

Aug 9, 2011 at 6:37 PM

Hopefully the mapping " ==>" should work automatically - we added this so people who upgrade can move to extensionless URLs with no fuss. If you don't experience this works for you, let me know - it should.

On the "query string to path" mapping you would probably need to write a http module that does the mapping - perhaps you can use the URL remapping module we have at docs as is or with minor modifications :)

Aug 9, 2011 at 6:42 PM

>> ==>

Version C1 2.1.3 and older C1 should do those redirects by default. 

As for other redirects, you can create an httpModule that would do it, or you can use f.e. UrlRewriting. I know that there's also a package for c1/or a sample on this forum how to do it, but I couldn't find it

Aug 9, 2011 at 8:44 PM

Wow! That's seamless. I hadn't even THOUGHT of typing in the extension and see if it works in my demo... and I only thought of the Friendly URL method while I was writing the blog post. Great job, guys! Lots of thought was put into this!

Aug 9, 2011 at 11:04 PM

Beautiful! I have modified the URL remapping module so that it dynamically converts query strings to pathInfo

It will convert: =>

I added one property to the object: RewriteQueryStringAsPathInfo 


    private class Remapping
        public string RequestPath { get; set; }
        public string RequestHost { get; set; }
        public bool RequestHostEndsWith { get; set; }

        public string RewritePath { get; set; }
        public string RewriteHost { get; set; }
        public bool RewriteQueryStringAsPathInfo { get; set; }

        public override string ToString()
            return string.Format("http://{0}{1} --> http://{2}{3}",
                (RequestHost ?? "*"),
                (RewriteHost ?? "*"),
                (RewritePath ?? "/*"));


Added a valid RemappingAttributeName:


    private readonly List<XName> _validRemappingAttributeNames = new List<XName> { "requestPathStartsWith", "requestPath", "requestHostEndsWith", "requestHost", "rewritePath", "rewriteHost", "rewriteQueryStringAsPathInfo" };


Changed one statement in the LoadRemappings() method:


Remapping remapping = new Remapping
                RequestPath = LowerValue(remappingElement, "requestPath"),
                RequestHost = LowerValueOrNull(remappingElement.Attribute("requestHost")),
                RequestHostEndsWith = AsBool(remappingElement, "requestHostEndsWith", false),
                RewritePath = ValueOrNull(remappingElement.Attribute("rewritePath")),
                RewriteHost = LowerValueOrNull(remappingElement.Attribute("rewriteHost")),
                RewriteQueryStringAsPathInfo = AsBool(remappingElement, "rewriteQueryStringAsPathInfo", false)


And changed a bit of code to the ExecuteRemappingsOnRequest method


newPathAndQuery = remapping.RewritePath ?? context.Request.Url.PathAndQuery;

 void ExecuteRemappingsOnRequest(object sender, EventArgs e)
	// ... Snip code
            if (remapping.RewriteQueryStringAsPathInfo) {
                string pathInfo = "";
                foreach (string key in context.Request.QueryString.AllKeys) {
                    // Change this line to fit how you want to deal with the Query String to PathInfo transition
		    pathInfo += "/" + key + "-" + context.Request.QueryString[key];
                newPathAndQuery = (remapping.RewritePath ?? context.Request.Url.AbsolutePath) + pathInfo;

            else {
                newPathAndQuery = remapping.RewritePath ?? context.Request.Url.PathAndQuery;
	// ... SNIP Code


Aug 10, 2011 at 11:44 AM

Nice! Your post is an excellent reference for others that wish to make this move - tnx :)

Nov 1, 2011 at 11:37 PM


Perhaps a slight tangent on the original question, but I haven't been able to find more information about what seems to be a fairly basic/fundamental question.

Out of the box, without any URL remapping modules, IIS configuration, etc., when do I get URLs with or without .aspx extensions?

- The default Starter site has very clean URLs (e.g. /3-Columns/Sub-page-1).

- When I add new pages to the Starter site, they also get clean URLs.

- When I add a new Website that uses a new Page Type and Layout, the URLs for those pages invariably get a .aspx extension.

- Similarly, and surprisingly, the default Frontpage of the Starter site has a URL Title 'Home', but the page can't be called with /Home, only with /Home.aspx (or, of course, just as the root of the site).


Is there a setting that controls whether the extension is required? I'd prefer not to have any extensions.

Thanks for any pointers.

Nov 18, 2011 at 1:01 AM

I'm not SURE this answers your question, but if you're running on IIS7 in integrated mode and you have double checked that you don't have an extension set in the System / URL Configuration area, you should be ready to go!


If you're running IIS6, I think there's a bit more work, but in IIS7, routing just works. Just double check you don't have any extension in the URL COnfiguration. Or if you want, you could choose your own extension ( .html for example )