Document and update API for `skip_parameter_encoding`

This commit changes `parameter_encoding` to `skip_parameter_encoding`.
`skip_parameter_encoding` will set encoding on all parameters to
ASCII-8BIT for a given action on a particular controller.  This allows
the controller to handle data when the encoding of that data is unknown,
for example file systems or truly binary parameters.
上级 85b2a3ea
......@@ -139,8 +139,8 @@ def self.make_response!(request)
end
end
def self.encoding_for_param(action, param) # :nodoc:
::Encoding::UTF_8
def self.binary_params_for?(action) # :nodoc:
false
end
# Delegates to the class' <tt>controller_name</tt>
......
module ActionController
# Allows encoding to be specified per parameter per action.
# Specify binary encoding for parameters for a given action.
module ParameterEncoding
extend ActiveSupport::Concern
......@@ -13,17 +13,32 @@ def setup_param_encode # :nodoc:
@_parameter_encodings = {}
end
def encoding_for_param(action, param) # :nodoc:
if @_parameter_encodings[action.to_s] && @_parameter_encodings[action.to_s][param.to_s]
@_parameter_encodings[action.to_s][param.to_s]
else
super
end
def binary_params_for?(action) # :nodoc:
@_parameter_encodings[action.to_s]
end
def parameter_encoding(action, param_name, encoding)
@_parameter_encodings[action.to_s] ||= {}
@_parameter_encodings[action.to_s][param_name.to_s] = encoding
# Specify that a given action's parameters should all be encoded as
# ASCII-8BIT (it "skips" the encoding default of UTF-8).
#
# For example, a controller would use it like this:
#
# class RepositoryController < ActionController::Base
# skip_parameter_encoding :show
#
# def show
# @repo = Repository.find_by_filesystem_path params[:file_path]
# end
#
# def index
# @repositories = Repository.all
# end
# end
#
# The show action in the above controller would have all parameter values
# encoded as ASCII-8BIT. This is useful in the case where an application
# must handle data but encoding of the data is unknown, like file system data.
def skip_parameter_encoding(action)
@_parameter_encodings[action.to_s] = true
end
end
end
......
......@@ -45,7 +45,7 @@ def parameters
query_parameters.dup
end
params.merge!(path_parameters)
params = set_custom_encoding(params)
params = set_binary_encoding(params)
set_header("action_dispatch.request.parameters", params)
params
end
......@@ -73,21 +73,16 @@ def path_parameters
private
def set_custom_encoding(params)
def set_binary_encoding(params)
action = params[:action]
params.each do |k, v|
if v.is_a?(String) && v.encoding != encoding_template(action, k)
params[k] = v.force_encoding(encoding_template(action, k))
if controller_class.binary_params_for?(action)
ActionDispatch::Request::Utils.each_param_value(params) do |param|
param.force_encoding ::Encoding::ASCII_8BIT
end
end
params
end
def encoding_template(action, param)
controller_class.encoding_for_param(action, param)
end
def parse_formatted_parameters(parsers)
return yield if content_length.zero? || content_mime_type.nil?
......
......@@ -69,7 +69,7 @@ def commit_cookie_jar! # :nodoc:
PASS_NOT_FOUND = Class.new { # :nodoc:
def self.action(_); self; end
def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end
def self.encoding_for_param(action, param); ::Encoding::UTF_8; end
def self.binary_params_for?(action); false; end
}
def controller_class
......
......@@ -4,6 +4,17 @@ class Utils # :nodoc:
mattr_accessor :perform_deep_munge
self.perform_deep_munge = true
def self.each_param_value(params, &block)
case params
when Array
params.each { |element| each_param_value(element, &block) }
when Hash
params.each_value { |value| each_param_value(value, &block) }
when String
block.call params
end
end
def self.normalize_encode_params(params)
if perform_deep_munge
NoNilParamEncoder.normalize_encode_params params
......
require "abstract_unit"
class ParameterEncodingController < ActionController::Base
parameter_encoding :test_bar, :bar, Encoding::ASCII_8BIT
parameter_encoding :test_baz, :baz, Encoding::ISO_8859_1
parameter_encoding :test_baz_to_ascii, :baz, Encoding::ASCII_8BIT
skip_parameter_encoding :test_bar
skip_parameter_encoding :test_all_values_encoding
def test_foo
render body: params[:foo].encoding
......@@ -13,16 +12,8 @@ def test_bar
render body: params[:bar].encoding
end
def test_baz
render body: params[:baz].encoding
end
def test_no_change_to_baz
render body: params[:baz].encoding
end
def test_baz_to_ascii
render body: params[:baz].encoding
def test_all_values_encoding
render body: ::JSON.dump(params.values.map(&:encoding).map(&:name))
end
end
......@@ -36,32 +27,18 @@ class ParameterEncodingTest < ActionController::TestCase
assert_equal "UTF-8", @response.body
end
test "properly transcodes ASCII_8BIT parameters into declared encodings" do
test "properly encodes ASCII_8BIT parameters into binary" do
post :test_bar, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
assert_response :success
assert_equal "ASCII-8BIT", @response.body
end
test "properly transcodes ISO_8859_1 parameters into declared encodings" do
post :test_baz, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
assert_response :success
assert_equal "ISO-8859-1", @response.body
end
test "does not transcode parameters when not specified" do
post :test_no_change_to_baz, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
test "properly encodes all ASCII_8BIT parameters into binary" do
post :test_all_values_encoding, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
assert_response :success
assert_equal "UTF-8", @response.body
end
test "respects different encoding declarations for a param per action" do
post :test_baz_to_ascii, params: { "foo" => "foo", "bar" => "bar", "baz" => "baz" }
assert_response :success
assert_equal "ASCII-8BIT", @response.body
assert_equal ["ASCII-8BIT"], JSON.parse(@response.body).uniq
end
test "does not raise an error when passed a param declared as ASCII-8BIT that contains invalid bytes" do
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册