1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# frozen_string_literal: true
module ActionDispatch
module Http
module 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
# Returns the parameter parsers.
attr_reader :parameter_parsers
end
self.parameter_parsers = DEFAULT_PARSERS
end
module ClassMethods
# Configure the parameter parser for a given MIME type.
#
# It accepts a hash where the key is the symbol of the MIME type
# and the value is a proc.
#
# original_parsers = ActionDispatch::Request.parameter_parsers
# xml_parser = -> (raw_post) { Hash.from_xml(raw_post) || {} }
# new_parsers = original_parsers.merge(xml: xml_parser)
# ActionDispatch::Request.parameter_parsers = new_parsers
def parameter_parsers=(parsers)
@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
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, params[:controller], params[:action])
set_header("action_dispatch.request.parameters", params)
params
end
alias :params :parameters
def path_parameters=(parameters) #:nodoc:
delete_header("action_dispatch.request.parameters")
parameters = set_binary_encoding(parameters, parameters[:controller], parameters[:action])
# 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.
# Returned hash keys are strings:
#
# {'action' => 'my_action', 'controller' => 'my_controller'}
def path_parameters
get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
end
private
def set_binary_encoding(params, controller, action)
return params unless controller && controller.valid_encoding?
if binary_params_for?(controller, action)
ActionDispatch::Request::Utils.each_param_value(params) do |param|
param.force_encoding ::Encoding::ASCII_8BIT
end
end
params
end
def binary_params_for?(controller, action)
controller_class_for(controller).binary_params_for?(action)
rescue NameError
false
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.
log_parse_error_once
raise ParseError
end
end
def log_parse_error_once
@parse_error_logged ||= begin
parse_logger = logger || ActiveSupport::Logger.new($stderr)
parse_logger.debug <<~MSG.chomp
Error occurred while parsing request parameters.
Contents:
#{raw_post}
MSG
end
end
def params_parsers
ActionDispatch::Request.parameter_parsers
end
end
end
end
|