views:

66

answers:

2

HTTP PUT isn't entirely cross browser so Rails (I'm using Rails 3) supports using POST and passing the _method query param. This is great, but it doesn't seem to work when sending JSON.

Example:

$.ajax({
    url: window.location.pathname,
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({_method:'PUT', page:{my_data: 1}),
    dataType: 'json'
});

When Rails sees this, it doesn't recognize the '_method' override because it's passed in the JSON format (perhaps that conversion is later?). Rails returns an error "No route matches ..." saying it can't find the route (to the resource), I assume because it doesn't match the REST update=HTTP PUT verb, I've even tried appending this to the URL: ?_method=PUT but got the same result.

The only thing that does seem to work is setting an HTTP header:

$.ajax({
    url: window.location.pathname,
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({my_data: 1}),
    dataType: 'json',
    beforeSend: function(xhr){
        xhr.setRequestHeader("X-Http-Method-Override","put");
    }
});

Is setting the HTTP override header the best way?

A: 

AJAX supports the PUT verb directly so why bothering with _method and custom HTTP headers:

$.ajax({
    url: window.location.pathname,
    type: 'PUT',
    contentType: 'application/json',
    data: JSON.stringify({ page: { my_data: 1 }),
    dataType: 'json'
});

Also make sure you are respecting the same origin policy or jquery might try to use JSONP which works only with GET verbs.

Darin Dimitrov
Not all browsers support `PUT` and `DELETE` methods in XmlHTTPRequest, but if you target modern ones go with it.
dev-null-dweller
HTTP spec describes PUT, POST, ... AJAX is just a collection of technologies. Though, you seem to be right (after much looking around) that *most* modern browsers support HTTP PUT.
Daniel Beardsley
It looks like most web browsers 4 years ago supported this (PUT POST DELETE for XmlHTTPRequests), so it's safe to say it works just fine. My original error was caused by mixing up PUT / POST when routing to rails.
Daniel Beardsley
A: 

I think your problem is that JSON.stringify converts the data structure to a JSON string, rather than to a nice URL. Try letting jQuery handle the encoding instead:

$.ajax({
    url: window.location.pathname,
    type: 'POST',
    contentType: 'application/json',
    data: {_method: 'PUT', page: {my_data: 1}},
    dataType: 'json'
});

Edit

Perhaps the easiest solution is to make a new helper function for PUT requests:

$.put = function(url, data, success, dataType) {
    $.ajax({
        url: url,
        data: data,
        success: success,
        dataType: dataType,
        type: 'POST',
        beforeSend: function(xhr){
            xhr.setRequestHeader("X-Http-Method-Override","put");
        }
    });
};

You could then call this with:

$.put(
    window.location.hostname,
    JSON.stringify({my_data: 1}),
    null,
    'json'
}
lonesomeday
Daniel Beardsley
Well, you need to put `_method` outside the JSON.stringify function. Perhaps `data: {_method: 'PUT', data: JSON.stringify({page: {my_data: 1}})},`
lonesomeday
You could do this, but I don't think Rails would do the nice automatic conversion of the request body to Ruby arrays and hashes, in fact, I think it will fail because request.body wouldn't be a valid JSON string.
Daniel Beardsley
Hmm, Rails plays funny then. I don't quite see the point of the _method parameter in this case. I have made an edit suggesting a `$.put` function to make your life easier, using the method you have found to work.
lonesomeday