在公司的一个项目中,无意中发现了 Rails 的这个 bug。
如果 params 属性不包含上传文件,一切嵌套属性表现正常。但是当 params 中包含文件的时候(multipart 请求),嵌套属性将出现问题,请参看以下测试代码
unless File.exist?('Gemfile')
File.write('Gemfile', <<-GEMFILE)
source 'https://rubygems.org'
gem 'rails', path: '~/code/rails'
gem 'arel', github: 'rails/arel'
gem 'rack', github: 'rack/rack'
gem 'i18n', github: 'svenfuchs/i18n'
GEMFILE
system 'bundle'
end
require 'bundler'
Bundler.setup(:default)
require 'rails'
require 'action_controller/railtie'
require 'yaml'
class TestApp < Rails::Application
config.root = File.dirname(__FILE__)
config.session_store :cookie_store, key: 'cookie_store_key'
config.secret_token = 'secret_token'
config.secret_key_base = 'secret_key_base'
config.logger = Logger.new($stdout)
Rails.logger = config.logger
routes.draw do
post '/' => 'test#test'
end
end
class TestController < ActionController::Base
include Rails.application.routes.url_helpers
def test
render json: my_params
end
private
def my_params
params.require(:thing).permit(
# :some_file,
:some_scalar_attribute,
:some_array_of_scalars => [],
:some_array_of_hashes_containing_scalars => [:scalar_one, :scalar_two]
)
end
end
require 'minitest/autorun'
require 'rack/test'
class BugTest < Minitest::Test
include Rack::Test::Methods
POST_DATA = {
"thing" => {
"some_file" => Rack::Test::UploadedFile.new(File.open(File.join(Rails.root, 'myfile.csv'))),
"some_scalar_attribute" => "foo",
"some_array_of_scalars" => ["b", "c"],
"some_array_of_hashes_containing_scalars" => [
{"scalar_one" => "d", "scalar_two" => "e"},
{"scalar_one" => "f", "scalar_two" => "g"}
]
}
}
JSON_DATA = '{"some_scalar_attribute":"foo","some_array_of_scalars":["b","c"],"some_array_of_hashes_containing_scalars":[{"scalar_one":"d","scalar_two":"e"},{"scalar_one":"f","scalar_two":"g"}]}'
def test_returns_success
post '/', POST_DATA
assert last_response.ok?
assert_equal JSON_DATA, last_response.body
end
private
def app
Rails.application
end
end
用 Ruby 的 mini test 测试此代码之后,测试将显示不通过。这里,我往 Rails 的 controller POST 了一个"some_array_of_hashes_containing_scalars"属性。它是一个 array of hash(杂凑数组)。当 Rails 接受到此杂凑数组的时候,params["some_array_of_hashes_containing_scalars"] 只接受到了数组中的最后一个杂凑,之前的杂凑都莫名其妙的被 Rails 过滤掉了。而当在 POST_DATA 中删除/注释掉“some_file”属性之后,测试通过。一切正常!