Rails将空数组转换为请求参数中的nil

我在我的应用程序中有一个Backbone模型,它不是一个典型的扁平对象,它是一个大型的嵌套对象,我们将嵌套的部分存储在MySQL数据库的TEXT列中。

我想在Rails API中处理JSON编码/解码,从外部看起来就像POST / GET这个大的嵌套JSON对象,即使它的一部分被存储为string化的JSON文本。

不过,我遇到了一个问题,Rails神奇地将空数组转换nil值。 例如,如果我POST这个:

 { name: "foo", surname: "bar", nested_json: { complicated: [] } } 

我的Rails控制器看到这个:

 { :name => "foo", :surname => "bar", :nested_json => { :complicated => nil } } 

所以我的JSON数据已经改变了..

有没有人遇到过这个问题? 为什么Rails会修改我的POST数据?

UPDATE

这是他们做的地方:

https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/request.rb#L288

这是〜他们为什么这样做:

https://github.com/rails/rails/pull/8862

所以现在的问题是,如何在嵌套的JSON API情况下最好地处理这个问题?

经过多次search,我发现你从Rails 4.1开始,可以完全使用deep_munge“feature”

 config.action_dispatch.perform_deep_munge = false 

我找不到任何文档,但您可以在这里查看这个选项的介绍: https : //github.com/rails/rails/commit/e8572cf2f94872d81e7145da31d55c6e1b074247

这样做可能存在安全风险,请参阅以下文档: https : //groups.google.com/forum/#!topic/rubyonrails- security/ t1WFuuQyavI

看起来这是一个已知的,最近引入的问题: https : //github.com/rails/rails/issues/8832

如果你知道空数组在哪里,你总是可以在前filter中使用params[:...][:...] ||= []

或者,您可以将BackBone模型修改为JSON方法,在发布之前使用JSON.stringify()将nested_json显式string化,并在before_filter中使用JSON.parse其手动parsing出来。

丑,但它会工作。

你可以自己重新parsing参数,如下所示:

 class ApiController before_filter :fix_json_params [...] protected def fix_json_params if request.content_type == "application/json" @reparsed_params = JSON.parse(request.body.string).with_indifferent_access end end private def params @reparsed_params || super end end 

这通过查找具有JSON内容types的请求,重新parsing请求主体,然后拦截params方法返回重新分析的参数(如果它们存在)。

我遇到了类似的问题。

通过发送空string作为数组的一部分来修复它。

所以最好你的参数应该喜欢

 { name: "foo", surname: "bar", nested_json: { complicated: [""] } } 

所以我不是发送空数组,而是总是在我的请求中传递(“”)来绕过深度进程。

这里(我相信)一个合理的解决scheme,不涉及重新parsing原始请求正文。 这可能无法正常工作,如果你的客户端是张贴表单数据,但在我的情况下,我POST JSON。

application_controller.rb

  # replace nil child params with empty list so updates occur correctly def fix_empty_child_params resource, attrs attrs.each do |attr| params[resource][attr] = [] if params[resource].include? attr and params[resource][attr].nil? end end 

然后在你的控制器….

 before_action :fix_empty_child_params, only: [:update] def fix_empty_child_params super :user, [:child_ids, :foobar_ids] end 

我碰到这个,在我的情况下,如果一个POSTed资源包含child_ids: []child_ids: nil我想更新意味着“删除所有的孩子”。 如果客户端不打算更新child_ids列表,那么不应该在POST主体中发送,在这种情况下, params[:resource].include? attr params[:resource].include? attr将是false ,请求参数将保持不变。

我遇到了一个类似的问题,发现传递一个空string的数组将被Rails正确处理,如上所述。 如果在提交表单时遇到此问题,则可能需要包含一个与数组参数匹配的空隐藏字段:

 <input type="hidden" name="model[attribute_ids][]"/> 

当实际参数为空时,控制器将总是看到一个空string的数组,从而保持提交无状态。

这是解决此问题的一种方法。

 def fix_nils obj # fixes an issue where rails turns [] into nil in json data passed to params case obj when nil return [] when Array return obj.collect { |x| nils_to_empty_arrays x } when Hash newobj = {} obj.each do |k,v| newobj[k] = nils_to_empty_arrays v end return newobj else return obj end end 

然后就这样做

 fixed_params = fix_nils params 

只要你有目的地没有nils在你的params。