May be such problem is not new, but I didn't find anything similar. I have such jQuery code:
$.ajax({
url : ('/people/'+id),
type : 'DELETE',
dataType : 'json',
success : function(e) {
table.getItems(currentPage);
}
});
My Rails controller looks like this:
def destroy
@person = Person.find(params[:id])
@person.destroy
respond_to do |format|
format.html { redirect_to(people_url) }
format.json { render :json => @person, :status => :ok }
end
end
This is working.
But when I use the following (as generated by standard), the success
callback doesn't get called:
def destroy
@person = Person.find(params[:id])
@person.destroy
respond_to do |format|
format.html { redirect_to(people_url) }
format.json { head :ok }
end
end
Tested under rails 3.0.3
, jQuery 1.4.2
and Firefox 3.6.13
.
Is there a significant difference in REST, and is there a way to utilize jQuery by using the scaffolded controller?
I have run across this a few times and the answer is deceptively simple.
You are using dataType : 'json'
in your $.ajax call, so jQuery is expecting a JSON response. With head :ok
Rails returns a response containing a single space (http://github.com/rails/rails/issues/1742), which is not accepted by jQuery as valid JSON.
So if you really expect to get either an error or a bare 200 OK header, just set dataType : 'html'
in your request and it should work (if you don't set dataType, jQuery will try to guess at what the type is based on response headers, etc., and could still guess json, in which case you'd still have this problem). If you actually expect to get JSON back, instead of using head :ok
render something valid with JSON (see comments) or just use head :no_content
as suggested by @Waseem
GearHead is correct, except that the jQuery parser is actually smart enough now to treat an empty string response as null and not attempt to parse it.
However if you have calls that will sometimes receive json and sometimes receive a head
response, and you do not have access to the server or do not want to change all your head
calls, you can do this alternate solution:
The problem is that Rails sends a single space as an empty response when you use head
(see here: How to return truly empty body in rails? i.e. content-length 0)
The relevant parts of the jQuery parseJSON function look like this at the time of this writing:
parseJSON: function( data ) {
if ( typeof data !== "string" || !data ) {
return null;
}
// Make sure leading/trailing whitespace is removed (IE can't handle it)
data = jQuery.trim( data );
// Attempt to parse using the native JSON parser first
if ( window.JSON && window.JSON.parse ) {
return window.JSON.parse( data );
}
As you can see, jQuery is testing whether the data is an empty string before trimming it. It then tries to JSON.parse("")
which you can see in your console results in an error, triggering the ajax error callback via a catch
statement.
There is a simple fix. jQuery allows you to use converters when one data type is requested and a different one is returned. See here for more details: http://api.jquery.com/extending-ajax/
Since the rails head
response renders as text, you can simply define a text to json converter that will trim the response prior to attempting to parse it. Just add this snippet:
// deal with rails ' ' empty response
jQuery.ajaxSetup({
converters: {
"text json": function (response) {
jQuery.parseJSON($.trim(response))
}
}
})
This is sometimes caused by an old version of the jQuery Validate Plugin. If you are using this plugin, this sometimes leads to this issue. There is an update that fixes this, if it applies to your case.
Alternatively, you can probably figure out what's going wrong by setting up an error handler via:
$.ajaxSetup()
or $.ajaxError()
This will probably return a parse error. The newer versions of jQuery are notorious for being very strict with regards to JSON parsing.
精彩评论