Rails - 表单在AJAX调用期间不提交任何数据 - 文件上传 - 422状态代码

时间:2016-02-10 08:15:24

标签: ruby-on-rails ajax forms

我在尝试使用bootsy gem上传文件时遇到了问题,但问题与bootsy本身无关。 Bootsy生成一个具有以下定义的表单:

<form class="bootsy-upload-form form-inline" id="new_image" data-type="json" enctype="multipart/form-data" action="/bootsy/image_galleries/67/images" accept-charset="UTF-8" data-remote="true" method="post">
<input name="utf8" value="✓" type="hidden">
<input name="authenticity_token" id="authenticity_token" value="1pQMR5j33OKupMg4TSnQafLQx1BxzWjkP1KqTfZafqDRfGqwB8r2J4FzRE+dyuoHVOw/W0qd1FZ1JoJsJFThDQ==" type="hidden">
...

当我尝试上传文件时,执行此行:

this.uploadInput.closest('form').submit();

我之前添加了警告,所以我会看到表单的序列化数据是如何显示的,所有字段都按预期显示(包括真实性标记等):

alert(this.uploadInput.closest('form').serialize());

提交表单时,在POST请求期间没有数据发送,只有标题,在浏览器检查器中看不到任何内容,在rails的日志文件中看不到任何内容,它只是这样:

Started POST "/bootsy/image_galleries/47/images" for ::1 at 2016-02-05 11:20:04 +0100
Processing by Bootsy::ImagesController#create as HTML
Parameters: {"image_gallery_id"=>"47"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 0ms (ActiveRecord: 0.0ms)
FATAL -- : 
ActionController::InvalidAuthenticityToken - ActionController::InvalidAuthenticityToken:
_ actionpack (4.2.4) lib/action_controller/metal/request_forgery_protection.rb:181:in `handle_unverified_request'_

我在表单中生成了真实性令牌,我在元标记中有令牌,一切看起来都很好,没有任何错误被抛出。我还尝试创建类似于我的真实项目的示例应用程序,它按预期工作,形成正常提交的数据 - 当我尝试比较HTML代码和附加到两者的javascript事件时,它们几乎相似,因为其他因为其他部分宝石像ajax_pagination等,但没有任何部分,这应该导致这样的行为。

我正在使用Rails 4.2.4,使用body元素上的属性data-no-turbolink禁用turbolinks,项目使用bootstrap并包含JS库,如jQuery,下划线,欧芹,momentjs。

我会很感激任何想法,可能出现的问题,为什么表单不应该提交任何数据,哪里可能是一个问题。提前感谢任何提示。

更新 为了澄清事情,我在使用AJAX发送之前拍摄了一个状态图片 - 这个javascript是jQuery的jquery_ujs = RoR适配器的一部分。您可以看到,该数据包含发送前的所有表单域: enter image description here

但是数据没有发送到服务器: enter image description here

另一方面,在我的第二个工作项目中,正在发送数据: enter image description here

更新2: 只是一些信息,bootsy gem,负责创建表单使用remotipart将文件附加到请求。仍然......我正在调试javascript并且无法识别问题。这两个项目都有相同版本的jquery和remotipart,也是相同版本的rails。看起来这将是一个谜。

更新3: 所以我几乎解决了这个问题 - 上传现在正在运行,看起来这是javascript库的顺序问题。我会在确定问题后立即发布结果 - 我会撤消更改并尝试再次修复。

6 个答案:

答案 0 :(得分:2)

问题在于application.js中包含的javascript库顺序。 Bootsy是在jquery-fileupload之后定义的。似乎Bootsy必须在它之前定义,通过这种设置,两者都运作良好。所以说:

//= require jquery-fileupload
//= require bootsy
//= require bootsy/locales/cs.js

之后(工作):

//= require bootsy
//= require bootsy/locales/cs.js
//= require jquery-fileupload

答案 1 :(得分:1)

似乎未按预期将真实性令牌发送到服务器。

我会查看浏览器中的“网络请求”标签,并检查帖子的正文。

我认为将processData设置为false是正确的。根据{{​​3}},

  

默认情况下,作为对象传入数据选项的数据(技术上,不是字符串)将被处理并转换为查询字符串

此外,您可以在检查令牌之前在控制器中添加它:

before_filter { puts params }

我会 在过滤前跳过真实性标记。

此外,在您的图像“但数据未发送到服务器:”,您正在查看标题。真实性令牌在身体中发送。

我认为问题在于表单的编码方式。你不应该编码它。将内容类型设置为multipart / form-data。内容类型目前设置为'application / x-www-form-urlencoded'。

服务器对表单进行URL解码,将所有字段合并为一个。这就是为什么它找不到真实性标记。

根据JQuery docs,您应该在ajax请求中将contentType设置为false。或者,您可以将其设置为multipart / form-data。

答案 2 :(得分:0)

默认情况下,表单只发送普通数据。如果您需要发送图像以及发布请求,您可以使用remotipart gem。它很棒。我自己用过它。

答案 3 :(得分:0)

我有两个理论,其中没有一个与你的形式无关:

第一理论:

随着您的问题更新,如果仔细观察,Cookie不会包含_csrf_token。如果未传递_csrf_token,则Rails将引发该错误消息。

我无法看到您的表单是否使用此值生成自己的隐藏字段。可能是你的一个项目这样做而另一个没有。你应该看看它。

如果这是问题,您可以找到各种解决方案。其中之一是在视图的HTML头中包含[csfr_meta_tag]。请查看this以获取更多详细信息。

第二种理论:

如果您正在执行CORS请求,您的浏览器会首先发出一个额外的OPTIONS请求,以检查您是否有权访问此资源。

显然,_session_id无法在CORS中发送,除非您启用它。为此,您需要处理OPTIONS请求。这是通过修改控制器Access-Control-Allow-OriginAccess-Control-Allow-Methods方法上的Access-Control-Allow-Headersbefore_filterafter_filter标头来完成的。

Here有一个要点,显示了一个例子(忘记skip_before_filter行!)。如果您想获得指定标头值的更多详细信息,请检查this资源输出。

这些是我的理论,希望能帮到你!

<强>更新

如果你的两个项目都在使用Rails v4.2,那么可行的项目可能会设置protect_from_forgery,如下所示:

protect_from_forgery
# OR maybe
protect_from_forgery with: :null_session

无效的那个已设置protect_from_forgery,如下所示:

protect_from_forgery with: :exception

它们之间的区别在于发现未经验证的请求时的应用程序行为。查看更多here

答案 4 :(得分:0)

在没有看到你的实际代码的情况下,我们会在猜测中并在黑暗中拍摄,但是看起来很明显,我认为你已经遵循了所有的宝石指令?安装生成器使用资产管道并设置初始化器等。这似乎相当明显,但你确定在应用程序中的模型上做白名单booty param是不行的吗? (根据https://github.com/volmer/bootsy

private

def post_params
  params.require(:post).permit(:title, :content, :bootsy_image_gallery_id)
end

答案 5 :(得分:-1)

CRSF身份验证问题是跨站点脚本问题。要解决此问题,请执行其中一项,

skip_before_action :verify_authenticity_token in your controller

或者您可以在应用程序控制器中对此行进行注释。

protect_from_forgery with: :exception  

Visit this link for more description on this