jQuery ajax 请求不会触发 Rails 控制器的 JS 响应?

Posted

技术标签:

【中文标题】jQuery ajax 请求不会触发 Rails 控制器的 JS 响应?【英文标题】:jQuery ajax request not triggering JS response from rails controller? 【发布时间】:2011-10-18 16:29:53 【问题描述】:

我有一个标准控制器,用于响应 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 方面还不够好,无法调整它来做我需要的。

【问题讨论】:

【参考方案1】:

这个解决方案对我不起作用(rails 3.1 + coffeescript)。找了好久,找到了不错的方法,分享一下:

只需在网址末尾添加“.js”即可。如此简单... ;-)

【讨论】:

没有更好的方法吗?奇怪! 哥们,你太棒了:) 只是在 URL 中添加 .js 的问题,意味着请求 URL 与用户的浏览器 URL 不同(因为它的末尾有一个 .js)。这导致通过 JS 返回的页面通过分页构建有趣的 URL。 @shlensky 推荐的添加 dataType: 'script' 似乎在不更改 URL 的情况下完美运行。【参考方案2】:

只需添加dataType: 'script'

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

【讨论】:

dataType: 'script' 用于在 Rails 中响应jsdataType: 'json' 用于响应json【参考方案3】:

您必须在发送 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);
  
);

【讨论】:

这是公认的答案,但自从“.js”概念可用以来,我已将其更改为@ndemoreau 的答案。【参考方案4】:

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

$.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

【讨论】:

【参考方案5】:

让我解释一下这里发生了什么。

我经常在 HTTP 中混淆客户端发送给服务器的 Accept 标头和 Content-type 标头。 Accept 标头用于告诉服务器哪些内容类型(application/json、application/javascript、application/octet-stream、audio/mpeg、image/png、multipart/alternative、text/plain、text/html、text/csv 、视频/mpeg 等)他们会接受。服务器发回一个响应,其中包括 Content-Type 标头,通知客户端内容的实际 Content Type。

HTTP 请求也可以指定 Content-Type,因为在表单数据中,可以有所有类型的数据,并且 Content-Type 标头可以通知服务器实际数据是什么(例如 multipart/form-data)。不同的媒体类型(如 multipart/form-data)称为 MIME。

现在 jQuery.ajax() 有另一个可以传递给它的与这个主题相关的参数:accepts, contentType, dataType。

如果您了解 Content-Type HTTP 标头,则 contentType 属性很清楚。它告诉服务器数据实际上是什么。 jQuery 中的默认值为“application/x-www-form-urlencoded; charset=UTF-8”,这在大多数情况下都可以。

请记住,Accept 标头告诉服务器它将接受什么 Content-Type。但是当您阅读有关 dataType 的 jQuery 文档时,它听起来非常相似:“您期望从服务器返回的数据类型。”那么有什么区别呢?

accept 属性允许您更改请求中的 Accept 标头。但是通过改变 dataType 也会改变 Accept 头,所以真的不需要改变 accept 属性; dataType 将更改 Accept 标头。 dataType 的好处是 ti 允许您在成功处理程序可用之前对响应进行预处理。

实际上,我们需要告诉 Rails 我们将接受什么作为响应头,因此我们修改了 dataType。例如,在 Rails 中,符号 :js 和 :json 对应于 HTTP Mime 类型:

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指定为脚本。正如其中一个答案所示,您可以这样做:

$.ajax(
  url: "users/populate_user,
  type: "POST",
  data: formdata,
  dataType: 'script'
);  

现在看看 Request Header 看起来有多漂亮:

注意将 dataType 指定为脚本如何将 Accept 标头更改为 application/javascript。还要注意 contentType 是“application/x-www-form-urlencoded; charset=UTF-8”。记得我说过这是默认的 Content-Type jQuery 在没有指定的情况下会使用吗?那么这个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 'users/populate_user', to: 'users#populate_user'

从 SO 复制和粘贴答案时,准确了解您在项目中使用的内容也很重要。

【讨论】:

漂亮的答案。【参考方案6】:

您可以检查它是否是 xhr 请求并呈现您喜欢的格式。例如;

if request.xhr?
  render :json => 
    :some_data => 'bla'
  
end

【讨论】:

【参考方案7】:

虽然可能没有直接回答这个问题,但我遇到了类似的问题,其他人可能会觉得这很有帮助。特别是如果你使用haml。

经过多次尝试(包括将 .js 附加到 url)后,唯一对我有用的是在返回脚本响应时禁用布局渲染器。

  respond_to do |format|
    format.html
    format.js  render layout: false 
  end

我的问题是 AJAX 响应是由 rails 布局模板机制生成的完整 html 页面。这意味着我的 thing.js.erb 中的 javascript 没有执行。禁用布局意味着只返回 javascript(您可以在开发人员窗格的浏览器网络选项卡中找到返回),这允许它被执行。

我使用 haml 作为我的默认渲染器,我相信这就是为什么我需要明确禁用 js 渲染中的布局(我曾假设它是自动的)。

因此,如果没有其他方法起作用并且您使用 haml,请尝试此操作。

【讨论】:

【参考方案8】:

contentType设置为"text/javascript"

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

【讨论】:

以上是关于jQuery ajax 请求不会触发 Rails 控制器的 JS 响应?的主要内容,如果未能解决你的问题,请参考以下文章

jQuery之Ajax--全局Ajax事件处理器

jquery 不会在 $.ajax 上为 rails 标准 REST DELETE 答案调用成功方法

jquery ajax post请求不接受绝对url rails应用程序

我的 jquery AJAX POST 请求无需发送真实性令牌(Rails)即可工作

存在中止调用时触发 jQuery 的 ajaxStop 事件

ajax调用后Jquery事件不会触发