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。