jQuery GET breaks FormUrlEncodedProcessor

Topics: Web Api, jQuery
Mar 24, 2011 at 12:52 PM

Hi all, I'm having a frustrating morning.

I have created a service which is operating fine when called from a browser, i.e. the methods that support GET are behaving correctly.

Let's say my service is at http://localhost/PersonService, and the service looks like:

[WebGet(UriTemplate="{name}", ResponseFormat=WebMessageFormat.Json)]
Person GetPerson (string name)
{
    ...
}

 which returns a JSON-serialised Person object.

From a browser I can call "http://localhost/PersonService/John" and get the JSON back fine, but when called from jQuery, using the following code:

    var url = "http://localhost/PersonService/John";

    $.ajax({
        url: url,
        context: document.body,
        success: function (person) {
            alert("Got a person from service!");
        },
        error: function (xhr, status, error) {
            alert(xhr.status + ": " + xhr.statusText);
        },
        datatype: 'json'
    });

 then a NullReferenceException occurs in Microsoft.ServiceModel.Dispatcher.HttpPipelineFormatter.DeserializeRequest(HttpRequestMessage, object[]) because parameters[0] is actually 'null'.

At first I thought it was jQuery's appending of the timestamp onto the Uri query string which was causing the problem, but when I view the System.Web.HttpContext.Current.Request.Url.AbsoluteUri at the point of the exception being thrown (i.e. inside the DeserializeRequest method) and then copy and paste that into a browser window, the browser request succeeds and returns the JSON Person object, but the AJAX request doesn't even hit my service code at all; the NullReferenceException causes the operation to abort and a HTTP 500 is returned.

I have a feeling I'm going to kick myself when I diagnose the problem, until then does anybody have any bright ideas?

Many thanks!

 

 

 

Mar 24, 2011 at 4:10 PM
Edited Mar 24, 2011 at 4:14 PM

Sorted it, and I think I know why it's broken but don't have the time to understand and debug the HTTP.sln code more thoroughly.

By default, jQuery sets the Content-Type header on the AJAX request to be 'application/x-www-form-urlencoded', while the browser (IE8) sets that header to be 'text/plain' with a text encoding.

As soon as I updated the jQuery to the following:

    $.ajax({
        type: 'GET',
        contentType: 'text/plain; charset=iso-8859-1',
        url: url,
        context: document.body,
        success: function (person) {
            alert("Got a person from service!");
        },
        error: function (xhr, status, error) {
            alert(xhr.status + ": " + xhr.statusText);
        },
        datatype: 'json'
    });

it started to work again.

As far as I can tell, the only reason why it works now is because the request isn't passed into the Microsoft.ServiceModel.Http.FormUrlEncodedProcessor.ReadFromStream(Stream, HttpRequestMessage) method. If you send a request as I implicitly was, with the Content-Type header as 'application/x-www-form-urlencoded' the "Parameter" property in this method is null, so calling Parameter.ParameterType causes it to blow.

Not kicking myself just yet, even if I should have picked up on the missing "contentType:" in the ajax request, and it does prevent coders getting sloppy, but perhaps this method needs a bit of extra null checking? It's not uncommon to 'GET' while passing form fields, search engines do this for bookmarking, but I'm not sure if this is simply a coding faux pas or if I'm misunderstanding the process.

Fantastic library besides, keep up the great work WCF people!