jQuery的ajax请求没有触发Rails控制器中的JS响应?

38

我有一个标准控制器,它已设置为响应HTML、JS和JSON请求:

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

现在我正在尝试使用 $.ajax 函数向该控制器发送请求(在这种特定情况下,我不能使用 :remote => true - 我正在尝试使 ajax 文件上传工作)。

$.ajax({
  url: $("form#new_picture").attr("action"),
  type: "POST",
  data: formdata,
  processData: false,
  contentType: false
});

问题是我的请求因某些原因被视为 HTML 请求。如何告诉 Rails 我想要一个 JS 响应?

顺便说一下,我在项目中使用 jquery_ujs,因此如果需要,我可以使用它提供的方法。我不太擅长 JS,无法将其调整以符合我在这里需要的条件。

8个回答

56

这个解决方案对我没有用(Rails 3.1 + CoffeeScript)。经过相当多的搜索,我找到了一个好方法,并想分享:

只需在URL末尾添加“.js”。如此简单... ;-)


1
没有更好的方法吗?奇怪! - hachpai
3
兄弟,你太棒了 :) - kamal
1
仅仅在URL后面添加“.js”的问题在于,请求的URL与用户浏览器的URL不同(因为它在末尾有一个“.js”)。这导致通过JS返回的页面进行分页时构建了有趣的URL。按照@shlensky的建议添加dataType: 'script'似乎完美地解决了这个问题,而不需要改变URL。 - Andrew K

38

只需添加dataType: 'script'即可

$.ajax({
  url: $("form#new_picture").attr("action"),
  type: "POST",
  data: formdata,
  processData: false,
  contentType: false,
  dataType: 'script'
});    

8
dataType: 'script' 用于响应 Rails 中的 jsdataType: 'json' 用于响应 json - dodgio

7

在发送ajax请求之前,您必须设置'accept'头,以便Rails知道如何响应。

$.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);
  }
});

1
这是被接受的答案,但由于“.js”概念可用,我已将其更改为@ndemoreau的答案。 - David Tuite

6
让我解释一下这里发生了什么。在HTTP中,我经常会混淆客户端发送到服务器的Accept header和Content-type Header。Accept header用于告诉服务器他们将接受哪些内容类型(例如application/json、application/javascript、application/octet-stream、audio/mpeg、image/png、multipart/alternative、text/plain、text/html、text/csv、video/mpeg等)。服务器会发送一个响应,其中包括Content-Type header,通知客户端实际内容的内容类型。
HTTP请求也可以指定Content-Type,因为在表单数据中,可能有各种类型的数据,而Content-Type header可以通知服务器数据的实际类型(例如multipart/form-data)。像multipart/form-data这样的不同媒体类型称为MIME。
现在,jQuery.ajax()有另一个与此主题相关的参数:accepts、contentType、dataType。
如果你理解Content-Type HTTP header,那么contentType attribute就很清楚了。它告诉服务器数据的实际类型。在jQuery中,默认值是"application/x-www-form-urlencoded; charset=UTF-8",对于大多数情况来说都可以。
记住,Accept头告诉服务器它将接受什么Content-Type。但是当您阅读jQuery文档的dataType时,它听起来非常相似:“您期望从服务器返回的数据类型。”那么区别在哪里?
accepts属性允许您更改请求中的Accept头。但是通过更改dataType,它也会更改Accept头,因此实际上没有必要更改accept属性;dataType将更改Accept头。 dataType的好处是可以在可用于成功处理程序之前预处理响应。
实际上,我们需要告诉Rails我们将接受什么作为响应头,因此我们修改dataType。 在Rails中,例如,符号:js和:json对应于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 )

因此,如果我们想在 respond_to 块中触发 :js 选项,那么我们需要在 jQuery 中指定 dataType 为 script。正如其中一个答案所示,您可以像这样完成它:
$.ajax({
  url: "users/populate_user,
  type: "POST",
  data: formdata,
  dataType: 'script'
});  

现在看看请求头有多漂亮:

enter image description here

注意指定dataType为script时,Accept标头更改为application/javascript。还要注意contentType为“application/x-www-form-urlencoded; charset=UTF-8”。记住我说过,如果没有指定,则这是jQuery将使用的默认Content-Type?好吧,此SO页面中提供的另一个答案也指定了此选项:
  contentType: false

根据jQuery文档:
自jQuery 1.6起,您可以传递false告诉jQuery不设置任何内容类型标头。
最后一点。在Rails控制器中,如果此控制器操作仅响应于:js,则无需指定:js标志。您可以从控制器中简单地省略respond_to:
  def populate_user
    @user = User.from_names(params[:name][:value]).first
  end

然后添加一个名为 users/populate_user.js.erb 的文件。同时确保你的路由设置为 post 请求:
post 'users/populate_user', to: 'users#populate_user'

从Stack Overflow复制和粘贴答案时,了解你在项目中使用的内容非常重要。


优美的答案。 - Jey Geethan

6

在表单data中添加参数format: 'js',并添加dataType: 'script',如下所示:

$.ajax({
  url: '/manager/consumers/url',
  type: 'GET',
  dataType: 'script',
  data: {
  authenticity_token: '<%= form_authenticity_token %>',
  param_1: '1',
  param_2: '2',
  format: 'js'
  }
});

还需在控制器中添加代码以取消渲染布局:
  respond_to do |format|
      format.xls 
      format.js { render :layout => false }
    end

3
尽管可能没有直接回答问题,但我遇到了类似的问题,并且其他人可能会发现这很有帮助。特别是如果您使用haml。
在尝试多次后(包括将.js附加到url),对我有效的唯一方法是在返回脚本响应时禁用布局渲染器。
  respond_to do |format|
    format.html
    format.js { render layout: false }
  end

我的问题在于,AJAX响应是由Rails布局模板机制生成的完整HTML页面。这意味着我在 thing.js.erb 中的JavaScript没有执行。禁用布局意味着只返回JavaScript(您可以在开发人员面板的浏览器网络选项卡中找到返回值),从而允许JavaScript被执行。
我使用haml作为默认渲染器,我认为这就是为什么我需要在js渲染中显式禁用布局(我假设它会自动完成)。
所以,如果其他方法都没能解决问题,并且您使用haml,请尝试此方法。

3
您可以检查它是否为xhr请求,并呈现您喜欢的格式。例如;
if request.xhr?
  render :json => {
    :some_data => 'bla'
  }
end

1

contentType设置为"text/javascript"

$.ajax({
  url: $("form#new_picture").attr("action"),
  type: "POST",
  data: formdata,
  processData: false,
  contentType: "text/javascript"
});   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接