kemal/src/kemal/param_parser.cr

112 lines
3.1 KiB
Crystal
Raw Normal View History

2016-07-17 11:43:13 +00:00
module Kemal
2017-10-06 11:53:53 +00:00
# Parses the request contents including query_params and body
# and converts them into a params hash which you can use within
# the environment context.
2016-07-17 11:43:13 +00:00
class ParamParser
URL_ENCODED_FORM = "application/x-www-form-urlencoded"
APPLICATION_JSON = "application/json"
2017-02-11 13:33:42 +00:00
MULTIPART_FORM = "multipart/form-data"
PARTS = %w(url query body json files)
2016-11-10 13:38:29 +00:00
# :nodoc:
2018-06-16 15:03:00 +00:00
alias AllParamTypes = Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Any) | Array(JSON::Any)
getter files
def initialize(@request : HTTP::Request, @url : Hash(String, String) = {} of String => String)
2016-07-17 11:43:13 +00:00
@query = HTTP::Params.new({} of String => Array(String))
@body = HTTP::Params.new({} of String => Array(String))
@json = {} of String => AllParamTypes
@files = {} of String => FileUpload
2016-07-17 11:43:13 +00:00
@url_parsed = false
@query_parsed = false
@body_parsed = false
@json_parsed = false
@files_parsed = false
2016-07-17 11:43:13 +00:00
end
2015-10-28 18:30:27 +00:00
private def unescape_url_param(value : String)
2019-08-03 16:35:40 +00:00
value.empty? ? value : URI.decode(value)
rescue
value
end
2017-12-08 18:35:02 +00:00
{% for method in PARTS %}
2017-10-06 11:55:37 +00:00
def {{method.id}}
# check memoization
return @{{method.id}} if @{{method.id}}_parsed
2016-03-06 20:39:10 +00:00
2017-10-06 11:55:37 +00:00
parse_{{method.id}}
# memoize
@{{method.id}}_parsed = true
@{{method.id}}
end
2016-07-17 11:43:13 +00:00
{% end %}
2015-10-28 18:30:27 +00:00
2017-02-13 20:39:40 +00:00
private def parse_body
content_type = @request.headers["Content-Type"]?
2017-02-11 13:33:42 +00:00
return unless content_type
if content_type.try(&.starts_with?(URL_ENCODED_FORM))
@body = parse_part(@request.body)
return
end
if content_type.try(&.starts_with?(MULTIPART_FORM))
parse_files
end
2016-07-17 11:43:13 +00:00
end
2017-02-13 20:39:40 +00:00
private def parse_query
2016-07-17 11:43:13 +00:00
@query = parse_part(@request.query)
end
2017-02-13 20:39:40 +00:00
private def parse_url
@url.each { |key, value| @url[key] = unescape_url_param(value) }
2017-02-11 13:33:42 +00:00
end
private def parse_files
return if @files_parsed
HTTP::FormData.parse(@request) do |upload|
next unless upload
filename = upload.filename
if !filename.nil?
@files[upload.name] = FileUpload.new(upload)
else
@body.add(upload.name, upload.body.gets_to_end)
end
end
@files_parsed = true
end
2016-07-17 11:43:13 +00:00
# Parses JSON request body if Content-Type is `application/json`.
2017-10-06 11:53:53 +00:00
#
# - If request body is a JSON `Hash` then all the params are parsed and added into `params`.
# - If request body is a JSON `Array` it's added into `params` as `_json` and can be accessed like `params["_json"]`.
2017-02-13 20:39:40 +00:00
private def parse_json
return unless @request.body && @request.headers["Content-Type"]?.try(&.starts_with?(APPLICATION_JSON))
2015-11-10 11:30:16 +00:00
body = @request.body.not_nil!.gets_to_end
2016-07-17 11:43:13 +00:00
case json = JSON.parse(body).raw
when Hash
json.each do |key, value|
2018-06-16 15:03:00 +00:00
@json[key] = value.raw
2016-07-17 11:43:13 +00:00
end
when Array
@json["_json"] = json
2015-11-10 11:30:16 +00:00
end
2015-11-06 18:24:38 +00:00
end
2017-02-13 20:39:40 +00:00
private def parse_part(part : IO?)
2018-11-01 11:29:05 +00:00
HTTP::Params.parse(part ? part.gets_to_end : "")
end
2017-02-13 20:39:40 +00:00
private def parse_part(part : String?)
2018-11-01 11:29:05 +00:00
HTTP::Params.parse part.to_s
2016-07-17 11:43:13 +00:00
end
end
2015-10-28 18:30:27 +00:00
end