Upgrade ParamParser to make it more convinient.

It now decouples `env.params` to `env.params.query`, `env.params.body`,
`env.params.json` and `env.params.url` and you still can access merged
values using `env.params.all`
This commit is contained in:
Fatih Kadir Akın 2016-03-06 13:22:24 +02:00
parent c7d2b33bb3
commit 8267ffe2c5
6 changed files with 96 additions and 45 deletions

View file

@ -3,19 +3,31 @@ require "./spec_helper"
describe "ParamParser" do describe "ParamParser" do
it "parses query params" do it "parses query params" do
route = Route.new "POST", "/" do |env| route = Route.new "POST", "/" do |env|
hasan = env.params["hasan"] hasan = env.params.query["hasan"]
"Hello #{hasan}" "Hello #{hasan}"
end end
request = HTTP::Request.new("POST", "/?hasan=cemal") request = HTTP::Request.new("POST", "/?hasan=cemal")
params = Kemal::ParamParser.new(request).parse query_params = Kemal::ParamParser.new(request).params.query
params["hasan"].should eq "cemal" query_params["hasan"].should eq "cemal"
end
it "parses url params" do
kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "POST", "/hello/:hasan" do |env|
"hello #{env.params.url["hasan"]}"
end
request = HTTP::Request.new("POST", "/hello/cemal")
# Radix tree MUST be run to parse url params.
io_with_context = create_request_and_return_io(kemal, request)
url_params = Kemal::ParamParser.new(request).params.url
url_params["hasan"].should eq "cemal"
end end
it "parses request body" do it "parses request body" do
route = Route.new "POST", "/" do |env| route = Route.new "POST", "/" do |env|
name = env.params["name"] name = env.params.query["name"]
age = env.params["age"] age = env.params.query["age"]
hasan = env.params["hasan"] hasan = env.params.body["hasan"]
"Hello #{name} #{hasan} #{age}" "Hello #{name} #{hasan} #{age}"
end end
@ -26,8 +38,11 @@ describe "ParamParser" do
headers: HTTP::Headers{"Content-Type": "application/x-www-form-urlencoded"}, headers: HTTP::Headers{"Content-Type": "application/x-www-form-urlencoded"},
) )
params = Kemal::ParamParser.new(request).parse query_params = Kemal::ParamParser.new(request).params.query
params.should eq({"hasan" => "cemal", "name" => "serdar", "age" => "99"}) query_params.should eq({"hasan" => "cemal"})
body_params = Kemal::ParamParser.new(request).params.body
body_params.should eq({"name" => "serdar", "age" => "99"})
end end
context "when content type is application/json" do context "when content type is application/json" do
@ -41,8 +56,8 @@ describe "ParamParser" do
headers: HTTP::Headers{"Content-Type": "application/json"}, headers: HTTP::Headers{"Content-Type": "application/json"},
) )
params = Kemal::ParamParser.new(request).parse json_params = Kemal::ParamParser.new(request).params.json
params.should eq({"name": "Serdar"}) json_params.should eq({"name": "Serdar"})
end end
it "parses request body for array" do it "parses request body for array" do
@ -55,8 +70,8 @@ describe "ParamParser" do
headers: HTTP::Headers{"Content-Type": "application/json"}, headers: HTTP::Headers{"Content-Type": "application/json"},
) )
params = Kemal::ParamParser.new(request).parse json_params = Kemal::ParamParser.new(request).params.json
params.should eq({"_json": [1]}) json_params.should eq({"_json": [1]})
end end
it "parses request body and query params" do it "parses request body and query params" do
@ -69,8 +84,11 @@ describe "ParamParser" do
headers: HTTP::Headers{"Content-Type": "application/json"}, headers: HTTP::Headers{"Content-Type": "application/json"},
) )
params = Kemal::ParamParser.new(request).parse query_params = Kemal::ParamParser.new(request).params.query
params.should eq({"foo": "bar", "_json": [1]}) query_params.should eq({"foo": "bar"})
json_params = Kemal::ParamParser.new(request).params.json
json_params.should eq({"_json": [1]})
end end
it "handles no request body" do it "handles no request body" do
@ -82,17 +100,26 @@ describe "ParamParser" do
headers: HTTP::Headers{"Content-Type": "application/json"}, headers: HTTP::Headers{"Content-Type": "application/json"},
) )
params = Kemal::ParamParser.new(request).parse url_params = Kemal::ParamParser.new(request).params.url
params.should eq({} of String => AllParamTypes) url_params.should eq({} of String => String)
query_params = Kemal::ParamParser.new(request).params.query
query_params.should eq({} of String => String)
body_params = Kemal::ParamParser.new(request).params.body
body_params.should eq({} of String => String)
json_params = Kemal::ParamParser.new(request).params.json
json_params.should eq({} of String => AllParamTypes)
end end
end end
context "when content type is incorrect" do context "when content type is incorrect" do
it "does not parse request body" do it "does not parse request body" do
route = Route.new "POST", "/" do |env| route = Route.new "POST", "/" do |env|
name = env.params["name"] name = env.params.body["name"]
age = env.params["age"] age = env.params.body["age"]
hasan = env.params["hasan"] hasan = env.params.query["hasan"]
"Hello #{name} #{hasan} #{age}" "Hello #{name} #{hasan} #{age}"
end end
@ -103,8 +130,11 @@ describe "ParamParser" do
headers: HTTP::Headers{"Content-Type": "text/plain"}, headers: HTTP::Headers{"Content-Type": "text/plain"},
) )
params = Kemal::ParamParser.new(request).parse query_params = Kemal::ParamParser.new(request).params.query
params.should eq({"hasan" => "cemal"}) query_params.should eq({"hasan" => "cemal"})
body_params = Kemal::ParamParser.new(request).params.body
body_params.should eq({} of String => String)
end end
end end
end end

View file

@ -15,7 +15,7 @@ describe "Kemal::RouteHandler" do
it "routes request with query string" do it "routes request with query string" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "GET", "/" do |env| kemal.add_route "GET", "/" do |env|
"hello #{env.params["message"]}" "hello #{env.params.query["message"]}"
end end
request = HTTP::Request.new("GET", "/?message=world") request = HTTP::Request.new("GET", "/?message=world")
io_with_context = create_request_and_return_io(kemal, request) io_with_context = create_request_and_return_io(kemal, request)
@ -26,7 +26,7 @@ describe "Kemal::RouteHandler" do
it "routes request with multiple query strings" do it "routes request with multiple query strings" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "GET", "/" do |env| kemal.add_route "GET", "/" do |env|
"hello #{env.params["message"]} time #{env.params["time"]}" "hello #{env.params.query["message"]} time #{env.params.query["time"]}"
end end
request = HTTP::Request.new("GET", "/?message=world&time=now") request = HTTP::Request.new("GET", "/?message=world&time=now")
io_with_context = create_request_and_return_io(kemal, request) io_with_context = create_request_and_return_io(kemal, request)
@ -37,7 +37,7 @@ describe "Kemal::RouteHandler" do
it "route parameter has more precedence than query string arguments" do it "route parameter has more precedence than query string arguments" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "GET", "/:message" do |env| kemal.add_route "GET", "/:message" do |env|
"hello #{env.params["message"]}" "hello #{env.params.url["message"]}"
end end
request = HTTP::Request.new("GET", "/world?message=coco") request = HTTP::Request.new("GET", "/world?message=coco")
io_with_context = create_request_and_return_io(kemal, request) io_with_context = create_request_and_return_io(kemal, request)
@ -48,8 +48,8 @@ describe "Kemal::RouteHandler" do
it "parses simple JSON body" do it "parses simple JSON body" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "POST", "/" do |env| kemal.add_route "POST", "/" do |env|
name = env.params["name"] name = env.params.json["name"]
age = env.params["age"] age = env.params.json["age"]
"Hello #{name} Age #{age}" "Hello #{name} Age #{age}"
end end
@ -68,7 +68,7 @@ describe "Kemal::RouteHandler" do
it "parses JSON with string array" do it "parses JSON with string array" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "POST", "/" do |env| kemal.add_route "POST", "/" do |env|
skills = env.params["skills"] as Array skills = env.params.json["skills"] as Array
"Skills #{skills.each.join(',')}" "Skills #{skills.each.join(',')}"
end end
@ -87,7 +87,7 @@ describe "Kemal::RouteHandler" do
it "parses JSON with json object array" do it "parses JSON with json object array" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "POST", "/" do |env| kemal.add_route "POST", "/" do |env|
skills = env.params["skills"] as Array skills = env.params.json["skills"] as Array
skills_from_languages = skills.map do |skill| skills_from_languages = skills.map do |skill|
skill = skill as Hash skill = skill as Hash
skill["language"] skill["language"]

View file

@ -8,7 +8,7 @@ describe "Views" do
it "renders file" do it "renders file" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "GET", "/view/:name" do |env| kemal.add_route "GET", "/view/:name" do |env|
name = env.params["name"] name = env.params.url["name"]
render "spec/asset/hello.ecr" render "spec/asset/hello.ecr"
end end
request = HTTP::Request.new("GET", "/view/world") request = HTTP::Request.new("GET", "/view/world")
@ -20,7 +20,7 @@ describe "Views" do
it "renders file with dynamic variables" do it "renders file with dynamic variables" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "GET", "/view/:name" do |env| kemal.add_route "GET", "/view/:name" do |env|
name = env.params["name"] name = env.params.url["name"]
render_with_base_and_layout "hello.ecr" render_with_base_and_layout "hello.ecr"
end end
request = HTTP::Request.new("GET", "/view/world") request = HTTP::Request.new("GET", "/view/world")
@ -32,7 +32,7 @@ describe "Views" do
it "renders layout" do it "renders layout" do
kemal = Kemal::RouteHandler::INSTANCE kemal = Kemal::RouteHandler::INSTANCE
kemal.add_route "GET", "/view/:name" do |env| kemal.add_route "GET", "/view/:name" do |env|
name = env.params["name"] name = env.params.url["name"]
render "spec/asset/hello.ecr", "spec/asset/layout.ecr" render "spec/asset/hello.ecr", "spec/asset/layout.ecr"
end end
request = HTTP::Request.new("GET", "/view/world") request = HTTP::Request.new("GET", "/view/world")

View file

@ -2,9 +2,10 @@
# information such as params, content_type e.g # information such as params, content_type e.g
class HTTP::Server class HTTP::Server
class Context class Context
def params def params
@request.url_params = route_lookup.params @request.url_params = route_lookup.params
@params ||= Kemal::ParamParser.new(@request).parse @params ||= Kemal::ParamParser.new(@request).params
end end
def redirect(url, status_code = 302) def redirect(url, status_code = 302)

View file

@ -5,16 +5,34 @@ require "json"
# context. # context.
alias AllParamTypes = Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Type) | Array(JSON::Type) alias AllParamTypes = Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Type) | Array(JSON::Type)
class Kemal::ParamContainer
getter url
getter query
getter body
getter json
def initialize(@url, @query, @body, @json)
end
def all
@url.merge(@query).merge(@body).merge(@json)
end
end
class Kemal::ParamParser class Kemal::ParamParser
URL_ENCODED_FORM = "application/x-www-form-urlencoded" URL_ENCODED_FORM = "application/x-www-form-urlencoded"
APPLICATION_JSON = "application/json" APPLICATION_JSON = "application/json"
def initialize(@request) def initialize(@request)
@params = {} of String => AllParamTypes @url = {} of String => String
@query = {} of String => String
@body = {} of String => String
@json = {} of String => AllParamTypes
end end
def parse def params
parse_request parse_request
Kemal::ParamContainer.new(@url, @query, @body, @json)
end end
def parse_request def parse_request
@ -22,22 +40,21 @@ class Kemal::ParamParser
parse_body parse_body
parse_json parse_json
parse_url_params parse_url_params
@params
end end
def parse_body def parse_body
return if (@request.headers["Content-Type"]? =~ /#{URL_ENCODED_FORM}/).nil? return if (@request.headers["Content-Type"]? =~ /#{URL_ENCODED_FORM}/).nil?
parse_part(@request.body) @body = parse_part(@request.body)
end end
def parse_query def parse_query
parse_part(@request.query) @query = parse_part(@request.query)
end end
def parse_url_params def parse_url_params
if params = @request.url_params if params = @request.url_params
params.each do |key, value| params.each do |key, value|
@params[key] = value @url[key as String] = value as String
end end
end end
end end
@ -52,18 +69,21 @@ class Kemal::ParamParser
body = @request.body as String body = @request.body as String
case json = JSON.parse(body).raw case json = JSON.parse(body).raw
when Hash when Hash
json.each do |k, v| json.each do |key, value|
@params[k as String] = v as AllParamTypes @json[key as String] = value as AllParamTypes
end end
when Array when Array
@params["_json"] = json @json["_json"] = json
end end
end end
def parse_part(part) def parse_part(part)
return unless part part_params = {} of String => String
HTTP::Params.parse(part) do |key, value| if part
@params[key] ||= value HTTP::Params.parse(part) do |key, value|
part_params[key as String] ||= value as String
end
end end
part_params
end end
end end

View file

@ -11,7 +11,7 @@ class HTTP::Request
private def check_for_method_override! private def check_for_method_override!
@override_method = @method @override_method = @method
if @method == "POST" if @method == "POST"
params = Kemal::ParamParser.new(self).parse_request params = Kemal::ParamParser.new(self).params.all
if params.has_key?("_method") && HTTP::Request.override_method_valid?(params["_method"]) if params.has_key?("_method") && HTTP::Request.override_method_valid?(params["_method"])
@override_method = params["_method"] @override_method = params["_method"]
end end