diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/http/parameters.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/http/parameters.rb | 117 |
1 files changed, 84 insertions, 33 deletions
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index c2f05ecc86..ad4aadacf5 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -1,27 +1,66 @@ -require 'active_support/core_ext/hash/keys' -require 'active_support/core_ext/hash/indifferent_access' - module ActionDispatch module Http module Parameters - PARAMETERS_KEY = 'action_dispatch.request.path_parameters' + extend ActiveSupport::Concern + + PARAMETERS_KEY = "action_dispatch.request.path_parameters" + + DEFAULT_PARSERS = { + Mime[:json].symbol => -> (raw_post) { + data = ActiveSupport::JSON.decode(raw_post) + data.is_a?(Hash) ? data : { _json: data } + } + } + + # Raised when raw data from the request cannot be parsed by the parser + # defined for request's content mime type. + class ParseError < StandardError + def initialize + super($!.message) + end + end + + included do + class << self + attr_reader :parameter_parsers + end + + self.parameter_parsers = DEFAULT_PARSERS + end + + module ClassMethods + def parameter_parsers=(parsers) # :nodoc: + @parameter_parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key } + end + end # Returns both GET and POST \parameters in a single hash. def parameters - @env["action_dispatch.request.parameters"] ||= begin - params = begin - request_parameters.merge(query_parameters) - rescue EOFError - query_parameters.dup - end - params.merge!(path_parameters) - end + params = get_header("action_dispatch.request.parameters") + return params if params + + params = begin + request_parameters.merge(query_parameters) + rescue EOFError + query_parameters.dup + end + params.merge!(path_parameters) + params = set_binary_encoding(params) + set_header("action_dispatch.request.parameters", params) + params end alias :params :parameters def path_parameters=(parameters) #:nodoc: - @env.delete('action_dispatch.request.parameters') - @env[PARAMETERS_KEY] = parameters + delete_header("action_dispatch.request.parameters") + + # If any of the path parameters has an invalid encoding then + # raise since it's likely to trigger errors further on. + Request::Utils.check_param_encoding(parameters) + + set_header PARAMETERS_KEY, parameters + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise ActionController::BadRequest.new("Invalid path parameters: #{e.message}") end # Returns a hash with the \parameters used to form the \path of the request. @@ -29,31 +68,43 @@ module ActionDispatch # # {'action' => 'my_action', 'controller' => 'my_controller'} def path_parameters - @env[PARAMETERS_KEY] ||= {} + get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {}) end - private + private - # Convert nested Hash to HashWithIndifferentAccess. - # - def normalize_encode_params(params) - case params - when Hash - if params.has_key?(:tempfile) - UploadedFile.new(params) - else - params.each_with_object({}) do |(key, val), new_hash| - new_hash[key] = if val.is_a?(Array) - val.map! { |el| normalize_encode_params(el) } - else - normalize_encode_params(val) - end - end.with_indifferent_access + def set_binary_encoding(params) + action = params[:action] + if controller_class.binary_params_for?(action) + ActionDispatch::Request::Utils.each_param_value(params) do |param| + param.force_encoding ::Encoding::ASCII_8BIT + end end - else params end - end + + def parse_formatted_parameters(parsers) + return yield if content_length.zero? || content_mime_type.nil? + + strategy = parsers.fetch(content_mime_type.symbol) { return yield } + + begin + strategy.call(raw_post) + rescue # JSON or Ruby code block errors + my_logger = logger || ActiveSupport::Logger.new($stderr) + my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}" + + raise ParseError + end + end + + def params_parsers + ActionDispatch::Request.parameter_parsers + end end end + + module ParamsParser + ParseError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("ActionDispatch::ParamsParser::ParseError", "ActionDispatch::Http::Parameters::ParseError") + end end |