Will "the Mighty" Strohl

Rewriting URLs to Always Have Subdomains (kinda) For WCF Calls

There's nothing wrong with accepting both "www.MyDomain.com" and "MyDomain.com", right?  Well, the answer can be yes.  But it may not be.  We ran into the latter...

We (RezHub.com) have a new DotNetNuke module that uses no server controls at all.  That's right. None.  It is completely HTML, JS, JSON, and jQuery.  It is an awesome custom module that was written for us by Arrow Designs (a.k.a. Arrow Nuke).  There are some really cool features that were improved upon greatly in comparison to our original search module. 

One of those features is the intellisense or auto-suggest feature in the textboxes. The previous module had a clunky but functional implementation.  This version has an incredibly fast and accurate version.  In the back end, it relies on calls using jQuery and JSON to retrieve data, depending on the keystrokes of the visitor.  It goes without saying, that we have a WCF web service sitting in the background listening for and responding to this request to fill the city information.

A well-known limitation (Microsoft calls it a security feature), is that WCF will only listen to one domain.  For example, this web service will only listen to either RezHub.com OR www.RezHub.com.  The one that is listened on depends on which URL is first used to hit the web service.  For example, if RezHub.com is used to hit the web service first, then www.RezHub.com will no longer respond.  In fact, the direct URL will return a HTTP 404 error.  This will happen in the opposite order, but same conditions if the service was first hit using www.RezHub.com.

Needless to say, that is an extremely frustrating scenario!  And I think we are all on the same boat if I were to say that this was an incredibly short-sighted implementation by the Microsoft team.  Ugh!  Talk about useless headaches and wasted developer hours!

There are many ways to deal with this, but the most elegant and universally friendly way to approach this appeared to be through the use of rewriting the URL.  Luckily, if you are using DotNetNuke, this is a feature that you are already using on each page request/response, and there is a provider model to plug in logic for this very specific scenario!  So, we have a framework in place that will already allow us to change any request beginning with "RezHub.com" to "www.RezHub.com" without having to rearchitect any of our existing code.

While you could just plug in your own HttpModule, I suggest recreating the UrlRewriteModule that is included in the core.  Just copy the class, and change the namespaces, then plug it in to your App_Code directory or existing assemblies.

Now, in the existing UrlRewriteModule code, there is a OnBeginRequest method.  Here is where all of the magic happens for DNN.  Near the top of this class, but below where the existing instantiations are, add the following code.

If Not Regex.IsMatch(app.Request.Url.AbsoluteUri, "^(http|https){1}://([A-Za-z0-9\-]+\.{1}){2}") And Not String.Equals(Request.ServerVariables("REMOTE_HOST"), "localhost") Then
    Response.StatusCode = 301
    Response.Status = "301 Moved Permanently"
    Response.AddHeader("Location", RewriteUrlForDomain(app.Request.Url.AbsoluteUri))
End If

The previous code snippet will look at the domain name of the incoming request, and then determine if it has a subdomain or not.  For example, "www" will be considered a subdomain.  If there isn't a subdomain, and if the current request is not from the "localhost", then we will send the current URL to a sub method.  The submethod will be described in a moment.  Just know that once the submethod is called, the visitor is then sent back the headers we created, sending them to the new URL.  The 301 code will tell the web servers to get rid of the previous URL and index the new one.

Here is the code for the actual URL rewrite.

Private Function RewriteUrlForDomain(ByVal URL As String) As String
    If Not Regex.IsMatch(URL, "^(http|https){1}://([A-Za-z0-9\-]+\.{1}){2}") Then
        Return Regex.Replace(URL, "^(http://|https://){1}(.*)$", "$1www.$2")
        Return URL
    End If
End Function

In the previous code snippet, we once again look to see if there is a subdomain passed (important).  If there isn't, then add the "www." to the front of the domain name.

The final step is to add the required XML to your web.config to switch the DNN HttpModule out for your own.  Put it in the <system.web><httpModules> node collection.  Your snippet should look something like the following:

<add name="UrlRewrite" type="YourCompany.HttpModules.UrlRewriteModule, YourCompany.AssemblyName" />

That's it!  Now, all URLs will have the expected "www." in front of the URL when it is needed.

Technorati Tags: , , , ,

blog comments powered by Disqus