Wednesday, January 5, 2011

URL Rewriting Module Interferes with WCF REST Service Processing

I spent most of yesterday and a good chunk of this morning troubleshooting a problem with a WCF REST service I’ve been trying to create.  Specifically, I had a brain-dead simple WCF service that looked like this:

    [ServiceContract(Namespace = "AjaxRoomService")]
    public interface IAjaxRoomService
    {
        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
        string SayHello();
    }

The implementation looked like this:

    public class AjaxRoomService : IAjaxRoomService
    {
        public string SayHello()
        {
            return "Hello";
        }
    }

And the actual .svc file looked like this:

<%@ ServiceHost Language="C#" Debug="true" Service="Alanta.Web.Services.AjaxRoomService" Factory="System.ServiceModel.Activation.WebServiceHostFactory" CodeBehind="AjaxRoomService.svc.cs" %>

Note that I was using the WebServiceHostFactory, so I didn’t need to have anything in my web.config file.  Simple, right? Everything should have worked, right?  But when I tried to call the service (i.e., by navigating to http://localhost:51150/Services/AjaxRoomService.svc/SayHello), I consistently received a 404 error.

I did my standard Google searches, but all the issues people described had to do with running the service in IIS, and I wasn’t even getting that far: I was just trying to get this running under Cassini, in Visual Studio. 

But after much troubleshooting and more than a little swearing, I finally put my finger on it.  We’re using a simple URL rewriting module that shouldn’t have been interfering with this, but it was.  The trouble was in this particular line of code in the URL rewriting module:

HttpContext.Current.RewritePath(rewrittenPath);

A debugger showed that it was rewriting the path from “/Services/AjaxRoomService.svc/SayHello” to “/Services/AjaxRoomService.svc/SayHello” – in other words, it wasn’t making any changes.  But as soon as I changed it to only rewrite the path if the path had actually changed, my problem went away:

// Only rewrite the path if the path has changed, as otherwise it interferes with .svc request processing.
if (string.Compare(HttpContext.Current.Request.Path, rewrittenPath, true) != 0)
{
    Debug.WriteLine(string.Format("Rewriting {0} to {1}", HttpContext.Current.Request.Path, rewrittenPath));
    HttpContext.Current.RewritePath(rewrittenPath);
}
Not exactly a dramatic discovery, but I figured it might benefit someone else at some point, so I’ll toss it out there for the Google indexer to discover, in the hopes that it helps someone else someday.