views:

70

answers:

3

I'm using jQuery.ajax() to make a PUT request to a REST web service, but seeing some really strange serialization behavior.

(Before you say it: Yes, I know that not all browsers support PUT -- this is just an example implementation for an api/framework, and ultimately will not be called by a browser, but rather by a server-side library that does support the extra http verbs.)

Here's the form:

<form action="/example/api/artist" method="put" id="update">
    First Name: <input type="text" name="firstname" /><br/>
    Last Name: <input type="text" name="lastname" /><br/>
    Address: <input type="text" name="address" /><br/>
    City: <input type="text" name="city" /><br/>
    State: <input type="text" name="state" /><br/>
    Postal Code: <input type="text" name="postalcode" /><br/>
    Email: <input type="text" name="email" /><br/>
    Phone: <input type="text" name="phone" /><br/>
    Fax: <input type="text" name="fax" /><br/>
    Password: <input type="text" name="thepassword" /><br/>
    <input type="hidden" name="debug" value="true" />
    <input type="submit" value="Update Artist" />
    <input type="reset" value="Cancel" id="updateCancel" />
</form>

And the JS:

$("#update").submit(function(e){
    e.preventDefault();
    var frm = $(this);
    $.ajax({
        url: frm.attr('action'),
        data:{
            firstname: $("#update input[name=firstname]").val(),
            lastname: $("#update input[name=lastname]").val(),
            address: $("#update input[name=address]").val(),
            city: $("#update input[name=city]").val(),
            state: $("#update input[name=state]").val(),
            postalcode: $("#update input[name=postalcode]").val(),
            email: $("#update input[name=email]").val(),
            phone: $("#update input[name=phone]").val(),
            fax: $("#update input[name=fax]").val(),
            thepassword: $("#update input[name=thepassword]").val()
        },
        type: frm.attr('method'),
        dataType: "json",
        contentType: "application/json",
        success: function (data, textStatus, xhr){
            console.log(data);
            reloadData();
        },
        error: function (xhr, textStatus, err){
            console.log(textStatus);
            console.log(err);
        }
    });
});

When using FireBug, I see the request go through as this:

firstname=Austin&lastname=Weber&address=25463+Main+Street%2C+Suite+C&city=Berkeley&state=CA&postalcode=94707-4513&email=austin%40life.com&phone=555-513-4318&fax=510-513-4888&thepassword=nopolyes

That's not horrible, but ideally I'd rather get %20 instead of + for spaces. I tried wrapping each field value lookup in an escape:

firstname: escape($("#update input[name=firstname]").val())

But that makes things worse:

firstname=Austin&lastname=Weber&address=25463%2520Main%2520Street%252C%2520Suite%2520C&city=Berkeley&state=CA&postalcode=94707-4513&email=austin%40life.com&phone=555-513-4318&fax=510-513-4888&thepassword=nopolyes

In this case, the value is being escaped twice; so first the space is encoded to %20, and then the % sign is escaped to %25 resulting in the %2520 for spaces, and %252C for the comma in the address field.

What am I doing wrong here?

+1  A: 

You're using the built in object serialization. Do it yourself in this case:

data: "firstname=" + $("#update input[name=firstname]").val() + "&lastname=" // so on and so on

or better yet, make a function do it

function mySerializer(obj) { // send this your {firstname: $(...)} pairs
   $.each(obj, function(i,v,) {  // etc, etc
  });
}
Dan Heberden
Using this method without the escape() function I still get the `%2520 ` junk. :(
Adam Tuttle
+2  A: 

+ is the normal escape character for spaces, even though it's not defined in RFC1738. It's been used like that for historical reasons since forever. Is it a big problem? I think a lot of your clients implementations may be expecting that + works as well.

Emil Vikström
Turns out the framework wasn't properly decoding the url variables. Fixing that properly unencodes the +s into spaces.
Adam Tuttle