sending json response - ruby-on-rails

I'm sending a request from jquery and expecting json response. Configuration is like this:
type: 'POST',
cache: false,
iframe: true,
dataType: "json",
Now there's a funny thing. When I return data as text, it works fine:
render :text => "{}"
But when I use :json option, firefox prompts me to download file containing exactly two characters, {}. Success callback is never invoked.
render :json => {}
Since there's already a workaround (see above), the question has only theoretical value. Did it ever happen to you?

It's probably due to that you are setting iframe to true, so firefox shows the download prompt, either set
iframe: false,
otherwise you have to make rails send the text/plain header by using
render :text => "{}"
Regards.

Related

Rails ajax:success callback not triggering in production

My ajax callbacks are not triggering in my production environment. However, they are trigering in development with no errors. I'll simplify things for the sake of discussion.
Let's say I have a link that uses remote: true:
<%= link_to "Add Foo", new_foo_path, remote: true, id: 'new-foo' %>
foos_controller.rb
class FoosController < ApplicationController
def new
#foo = Foo.new
render partial: 'form' if request.xhr?
end
end
Using Chrome's console, I bind to ajax:success:
$(document).on("ajax:success", "#new-foo", function() { console.log("success!"); });
In development this works fine; I get the "success!" message in the Chrome console.
In production, however, it does not. The request is being made, the response is the form partial, but the callback does not fire.
Any ideas?
PS. The following does not work either.
$("#new-foo").bind("ajax:success", function() { console.log("success!"); })
There is no changes to config/environments/production.rb.
EDIT: It turns out ajax:error is being triggered when I click the link in production.
$(document).on("ajax:error", "#new-foo", function() { console.log("error"); });
My production logs don't show any errors, and the network tab in Chrome developer tools is showing a 200 OK response with the partial as the body.
However, the Content-Type header is text/javascript in production but text/html in development. Why is the same code responding with text/javascript in production?
The problem was that the browser was trying to execute the response body as JavaScript since the Content-Type header being sent back was text/javascript rather than text/html.
There are a number of ways to fix this. I chose to use jQuery's $.ajax function to set the dataType of all my ajax calls. This will ask the server to send back text/html.
$.ajaxSetup
dataType: 'html'
However, there are a couple other ways to ask the server for a specific response. See this StackOverflow answer for details.
There is still a lingering question, though. Why was the same code sending back text/html in development but text/javascript in production?
In case Google brought you here (like it did to me) because ajax:success is not working for you in a slightly different situation - when you're using button_to instead of link_to, this might save you some head banging.
Assuming you have this modified ERB:
<%= button_to "Add Foo", new_foo_path, remote: true, class: 'new-foo' %>
This JS won't work:
$(document).on("ajax:success", ".new-foo", function() { console.log("success!"); });
This won't work since the class new-foo is rendered on the input tag, while the data-remote="true" is rendered on the form tag. The ajax:success event fires on the form tag as a result -- not the child input tag, and that's why the console.log will never get called.
To fix this, change the class to form_class in the ERB:
<%= button_to "Add Foo", new_foo_path, remote: true, form_class: 'new-foo' %>

rails ajax request

I have a problem when sending the Ajax request. When you click on the link request is sent to the server 3 times and the answer did not come.
Why the request is sent three times to undermine?
Where did I go wrong in the formation of a query?
code:
run.html.erb
...
<%= link_to "Next", "#", :id => 'next', :class =>
...
run.js.erb
(function(){
$("#next").click(function(){
$.ajax({
type: 'POST',
url: '/engine/tff',
success: function(data){
alert("ok");
$("#question").html(data);
}
});
return false;
});
});
controller
def tff
respond_to do |format|
format.js render :text => "hello"
end
end
I am guessing the click event is being bound multiple times probably cos the script is being called multiple times. Not sure what is the reason for the multiple calls as I am not familiar with rails.
what you could do to avoid it is unbind the click event before binding it or use the on api.
function ajaxRequest(){
$.ajax({
type: 'POST',
url: '/engine/tff',
success: function(data){
alert("ok");
$("#question").html(data);
}
});
return false;
}
$("#next").unbind("click").bind("click",ajaxRequest);
or
$(document).on("click","#next",ajaxRequest);
also not sure if you were trying to bind on document ready and there is a typo in your code. but it should be wrapped like this:
$(function(){
$("#next").click(ajaxRequest);
});
One thing I ran into recently was that jquery tries to run the result, as the default dataType interprets the .js as a script, not text. You might need to add a dataType: "text" to the ajax call. I don't see how this translates into three calls though.
I thought returning false was supposed to prevent the click from moving on, but perhaps it is better form to use preventDefault() as apneadiving suggests.

Rails 3.1 with latest jQuery-UJS IE9 wants to save response as .js

I have a simple form for submitting an image like so:
<%= form_tag avatar_item_path(#tutorial, :format => :js), :method => :post, :remote => true do %>
<%= file_field_tag :item_avatar, :name => "item_image[image]" %>
<% end %>
(Dragonfly handles the image, in case that's somehow relevant)
My controller action is called update_avatar and it has no render or respond_to logic (it just calls find and save methods) so by default on a js request update_avatar.js.erb is rendered.
update_avatar.js.erb contains a js function and $(function () { // some code here })
In FF, Chrome and Safari this works fine.
The problem: IE9 wants to save the response as avatar.js
Using the developer tools I can see that the request is sent and received correctly and the response content-type is "text/javascript; charset=utf-8"
I've tried manually setting the content-type to application/javascript and this had no effect.
As others have suggested elsewhere, if I set the content-type to text/html IE9 will not prompt me to save it but in this case none of the browsers will actually evaluate the js in the response (I put the js in script tags).
From searching it seems that about a year ago others were experiencing the same issue and their solution was to upgrade jquery, since a year has passed I have a much newer version. Perhaps this is a regression?
I'm using the latest jquery-rails gem , 1.0.19, which includes jquery 1.7.1 and also includes the latest version of https://github.com/rails/jquery-ujs (I did a diff to make sure).
Using Jquery File Upload
https://github.com/blueimp/jQuery-File-Upload (basic version, not UI version)
Make sure the url has the js format explicitly stated and set the dataType option to 'script':
$('#item_avatar').fileupload({
url: "<%= avatar_item_path(item, :format => :js) %>",
dataType: 'script'
});
In your controller set the response content-type to text/plain:
respond_to do |format|
format.js { render :content_type => "text/plain" } # needed for IE
end
The js response should now properly eval in all browsers!

jQuery ajax request not triggering JS response from rails controller?

I have a standard controller which is set up to respond to HTML, JS and JSON requests:
def picture
#picture = Picture.new params[:picture]
respond_to do |format|
if #picture.save
format.html do
logger.debug "In the HTML responder"
redirect_to #picture
end
format.json { render json: #picture, status: :created, location: #picture }
format.js { render :nothing => true }
else
# you get the idea
end
end
end
Now I'm trying to send a request to that controller with the $.ajax function (I can't use :remote => true in this specific situation - I'm trying to make ajax file upload work).
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: false
});
The problem is that my request is being treated as a HTML request for some reason. How do I tell rails that I want a JS response?
By the way, I'm using jquery_ujs in my project so I have access to the methods it provides if necessary. I'm not really good enough at JS to tweak that to do what I need here.
This solution didn't work for me (rails 3.1 + coffeescript). After searching quite a lot, I found the good way to do it and I wanted to share:
Just add ".js" to the end of the url. So simple... ;-)
Just add dataType: 'script'
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: false,
dataType: 'script'
});
You have to set the 'accept' header before sending the ajax request so that Rails knows how to respond.
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: false,
beforeSend: function(xhr, settings) {
xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
}
});
Add dataType: 'script' and in data of form add the parameter format: 'js' like this:
$.ajax({
url: '/manager/consumers/url',
type: 'GET',
dataType: 'script',
data: {
authenticity_token: '<%= form_authenticity_token %>',
param_1: '1',
param_2: '2',
format: 'js'
}
});
also add in the controller to not render layout:
respond_to do |format|
format.xls
format.js { render :layout => false }
end
Let me explain what is going on here.
I often get the Accept header and Content-type Header that a client sends to the server confused in HTTP. The Accept header is used to tell the server what content types (application/json, application/javascript, application/octet-stream, audio/mpeg, image/png, multipart/alternative, text/plain, text/html, text/csv, video/mpeg, etc) they'll accept. The server sends back a response, which includes the Content-Type header notifying the client of the actual Content Type of the content.
HTTP requests can also specify Content-Type, because in form data, there could be all types of data, and the Content-Type header can notify the server what the data actually is (e.g. multipart/form-data). The different media types like multipart/form-data are known as MIME.
Now jQuery.ajax() has another of parameters you can pass it related to this topic: accepts, contentType, dataType.
The contentType attribute is clear if you understand the Content-Type HTTP header. It tells the server what the data actually is. The default in jQuery is "application/x-www-form-urlencoded; charset=UTF-8", which is fine for most cases.
Remember that the Accept header tells the server what Content-Type it will accept. But when you read the jQuery documentation for dataType, it sounds quite similar: "The type of data that you're expecting back from the server." So what is the difference?
The accepts attribute allows you to change the Accept header in the request. But by changing the dataType it will change the Accept header too, so there is really no need to change the accept attribute; the dataType will change the Accept header. The benefit of dataType is ti allows you to pre-process the response before being available to the succes handler.
In effect, we need to tell Rails what we will accept as a response header, so we modify the dataType. In Rails the symbols :js and :json, for example, correspond to a HTTP Mime Type:
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
Thus, if we want to trigger the :js option in the respond_to block, then we need to specify the dataType in jQuery as script. And as one of the answers illustrates, you do it like this:
$.ajax({
url: "users/populate_user,
type: "POST",
data: formdata,
dataType: 'script'
});
Now look how beautiful the Request Header looks:
Notice how specifying the dataType as script changed the Accept header to application/javascript. Also notice that the contentType is "application/x-www-form-urlencoded; charset=UTF-8". Remember I said this is the default Content-Type jQuery will use if none is specified? Well another answer provided in this SO page specified this option as well:
contentType: false
According to the jQuery documentation:
As of jQuery 1.6 you can pass false to tell jQuery to not set any
content type header.
One last point. In your Rails controller, you do not need to specify the :js flag if this controller action is only ever going to be responding to :js. You can simply omit the respond_to from the controller:
def populate_user
#user = User.from_names(params[:name][:value]).first
end
And then add a users/populate_user.js.erb file. Also make sure your route is set up for a post request:
post 'users/populate_user', to: 'users#populate_user'
When copying and pasting answers from SO, it is also important to understand exactly what you are using in your project.
You can check whether it is a xhr request and render the format you prefer. For Example;
if request.xhr?
render :json => {
:some_data => 'bla'
}
end
Although possibly not directly answering the question, I came across this with a similar problem and others may find this helpful. Especially if you use haml.
After many attempts (including appending .js to the url) the only thing that worked for me was disabling the layout renderer when returning a script response.
respond_to do |format|
format.html
format.js { render layout: false }
end
My problem was that the AJAX response was a full html page generated by the rails layout template mechanism. This meant my javascript in my thing.js.erb wasn't executing. Disabling the layout meant only the javascript was returned (you can find the return in you browsers network tab of the developer pane) which allowed it to be executed.
I'm using haml as my default renderer and I believe that is why I required explicitly disabling the layout in js renders (I had assumed it would be automatic).
So if nothing else has worked and you use haml, try this.
Set contentType as "text/javascript"
$.ajax({
url: $("form#new_picture").attr("action"),
type: "POST",
data: formdata,
processData: false,
contentType: "text/javascript"
});

Weird "406 not acceptable" error

When I try to hit this action via Javascript, I get a 406 Not Acceptable error:
def show
#annotation = Annotation.find_by_id(params[:id])
respond_to do |format|
format.html {
if #annotation.blank?
redirect_to root_path
else
redirect_to inline_annotation_path(#annotation)
end
}
format.js {
if params[:format] == "raw"
render :text => #annotation.body.to_s
else
render :text => #annotation.body.to_html
end
}
end
end
This is from jQuery, but I'm doing the right beforeSend stuff:
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader("Accept", "text/javascript");
},
cache: false
});
Here are my request headers:
Host localhost:3000
User-Agent Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
Accept text/javascript
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
Connection keep-alive
X-Requested-With XMLHttpRequest
Content-Type application/x-www-form-urlencoded
I cracked the case!
I was sending a format parameter with my get request in order to tell the server to send me markdown instead of HTML. Here's my Javascript:
$.get("/annotations/" + annotation_id, {format: 'raw'}, function(data) {
});
and then I was looking for this parameter in the format.js block:
format.js {
if params[:format] == "raw"
render :text => #annotation.body.to_s
else
render :text => #annotation.body.to_html
end
}
but apparently a format parameter confuses the respond_to block. I changed it from {format: 'raw'} to {markdown: 'true'} and it works.
I guess this is a bug in Rails?
include "format.js" in your respond_to block
This happened to me when using HTTPRiot connecting to a JSON rendering web app from an iPhone app. It appears that the issue is due to Rails expecting an Accept HTTP header that it is well, comfortable with. As such, I used Firefox's LiveHTTPHeaders extension to see what headers work without a 406. In any case the Accept string that worked was:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Another area that I would examine is the JSON-producing controller. If the controller is missing a format directive to specify it can return JSON in response, that too may cause a 406 error.
Check your application.js for require jquery_ujs
//= require jquery_ujs
Can you try without setting the Accept Header? It should actually work even without the Accept header.
Just use this code in controller action method format block:
format.js { render :nothing => true }
If you are using jRails this was causing a lot of problems for me, here is my application.js file:
$(document).ready(function () {
$.ajaxSetup({
beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, */*");
}
});
});
Is this served through Apache? You may want to take a look at http://forums.alwayswebhosting.com/showthread.php?p=8381, which describes scenarios where security policy interferes with requests.
EDIT: The URL referenced above advocates turning off request-sniffing security policy across an entire site, which makes the site vulnerable. Setting the SecFilterEngine option to Off in the .htaccess, which is what is prescribed in the URL, should be done only to zero in on the source of the problem. It should not be considered a long term solution.
For me it was a simple before_filter that was restricting a action that renders a js file, once I added the :except => [:action] to the before_filter block it was fine.
i was calling the url for js format but had this in the controller:
respond_to do |format|
format.html
end
this worked fine in Safari, but not with Firefox.
naturally i was missing something, should be:
respond_to do |format|
format.html
format.js
end
and now both browsers are fine.
I hit this problem when I forgot to add :remote => true to my Ajax form.
Another insidious "Gotcha" here is if you call your controller with a form_for that accidentally has an additional not-necessary parameter. The additional parameter can be interpreted by Rails as a format request, and you'll get the 406.
Example:
= form_for parking_permit, url: permit_group_parking_permits_path(#permit_group.id, useless_id), method: :get do |f|
...
In the example above, you RAKE ROUTES and determine that permit_group_parking_permits_path does NOT need anything but the permit_group id...
If you see your method being called such as /model/action.1?blahblahblah that ".1" is your tip-off.
This will return the 406. We won't talk about how much time I spent on this once.

Resources