For HTTP POSTs, map JSON request bodies to controller keyword arguments.

Fixes-bug: 1336943
Change-Id: I2e59e5d43d87a5279c41b155188ebe3281de0e11
This commit is contained in:
Ryan Petrello
2014-09-23 10:35:37 -04:00
parent e756446ab6
commit 921a8b77dc
2 changed files with 107 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ import six
from webob import (Request as WebObRequest, Response as WebObResponse, exc, from webob import (Request as WebObRequest, Response as WebObResponse, exc,
acceptparse) acceptparse)
from webob.multidict import NestedMultiDict
from .compat import urlparse, unquote_plus, izip from .compat import urlparse, unquote_plus, izip
from .secure import handle_security from .secure import handle_security
@@ -499,6 +500,14 @@ class PecanBase(object):
# fetch any parameters # fetch any parameters
if req.method == 'GET': if req.method == 'GET':
params = dict(req.GET) params = dict(req.GET)
elif req.content_type in ('application/json',
'application/javascript'):
try:
if not isinstance(req.json, dict):
raise TypeError('%s is not a dict' % req.json)
params = dict(NestedMultiDict(req.GET, req.json))
except (TypeError, ValueError):
params = dict(req.params)
else: else:
params = dict(req.params) params = dict(req.params)

View File

@@ -440,6 +440,16 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('index: 4') assert r.body == b_('index: 4')
def test_explicit_json_kwargs(self):
r = self.app_.post_json('/', {'id': '4'})
assert r.status_int == 200
assert r.body == b_('index: 4')
def test_path_with_explicit_json_kwargs(self):
r = self.app_.post_json('/4', {'id': 'four'})
assert r.status_int == 200
assert r.body == b_('index: 4')
def test_multiple_kwargs(self): def test_multiple_kwargs(self):
r = self.app_.get('/?id=5&dummy=dummy') r = self.app_.get('/?id=5&dummy=dummy')
assert r.status_int == 200 assert r.status_int == 200
@@ -450,6 +460,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('index: 6') assert r.body == b_('index: 6')
def test_json_kwargs_from_root(self):
r = self.app_.post_json('/', {'id': '6', 'dummy': 'dummy'})
assert r.status_int == 200
assert r.body == b_('index: 6')
# multiple args # multiple args
def test_multiple_positional_arguments(self): def test_multiple_positional_arguments(self):
@@ -477,6 +492,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('multiple: five, six') assert r.body == b_('multiple: five, six')
def test_positional_args_with_json_kwargs(self):
r = self.app_.post_json('/multiple', {'one': 'five', 'two': 'six'})
assert r.status_int == 200
assert r.body == b_('multiple: five, six')
def test_positional_args_with_url_encoded_dictionary_kwargs(self): def test_positional_args_with_url_encoded_dictionary_kwargs(self):
r = self.app_.post('/multiple', {'one': 'Five%20', 'two': 'Six%20%21'}) r = self.app_.post('/multiple', {'one': 'Five%20', 'two': 'Six%20%21'})
assert r.status_int == 200 assert r.status_int == 200
@@ -527,6 +547,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('optional: 4') assert r.body == b_('optional: 4')
def test_optional_arg_with_json_kwargs(self):
r = self.app_.post_json('/optional', {'id': '4'})
assert r.status_int == 200
assert r.body == b_('optional: 4')
def test_optional_arg_with_url_encoded_kwargs(self): def test_optional_arg_with_url_encoded_kwargs(self):
r = self.app_.post('/optional', {'id': 'Some%20Number'}) r = self.app_.post('/optional', {'id': 'Some%20Number'})
assert r.status_int == 200 assert r.status_int == 200
@@ -537,6 +562,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('optional: 5') assert r.body == b_('optional: 5')
def test_multiple_positional_arguments_with_json_kwargs(self):
r = self.app_.post_json('/optional/5', {'id': 'five'})
assert r.status_int == 200
assert r.body == b_('optional: 5')
def test_multiple_positional_url_encoded_arguments_with_kwargs(self): def test_multiple_positional_url_encoded_arguments_with_kwargs(self):
r = self.app_.post('/optional/Some%20Number', {'id': 'five'}) r = self.app_.post('/optional/Some%20Number', {'id': 'five'})
assert r.status_int == 200 assert r.status_int == 200
@@ -557,6 +587,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('optional: 7') assert r.body == b_('optional: 7')
def test_optional_arg_with_multiple_json_kwargs(self):
r = self.app_.post_json('/optional', {'id': '7', 'dummy': 'dummy'})
assert r.status_int == 200
assert r.body == b_('optional: 7')
def test_optional_arg_with_multiple_url_encoded_dictionary_kwargs(self): def test_optional_arg_with_multiple_url_encoded_dictionary_kwargs(self):
r = self.app_.post('/optional', { r = self.app_.post('/optional', {
'id': 'Some%20Number', 'id': 'Some%20Number',
@@ -621,6 +656,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('multiple_optional: 1, None, None') assert r.body == b_('multiple_optional: 1, None, None')
def test_multiple_optional_positional_args_with_json_kwargs(self):
r = self.app_.post_json('/multiple_optional', {'one': '1'})
assert r.status_int == 200
assert r.body == b_('multiple_optional: 1, None, None')
def test_multiple_optional_positional_args_with_encoded_dict_kwargs(self): def test_multiple_optional_positional_args_with_encoded_dict_kwargs(self):
r = self.app_.post('/multiple_optional', {'one': 'One%21'}) r = self.app_.post('/multiple_optional', {'one': 'One%21'})
assert r.status_int == 200 assert r.status_int == 200
@@ -631,6 +671,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('multiple_optional: 1, None, None') assert r.body == b_('multiple_optional: 1, None, None')
def test_multiple_optional_positional_args_and_json_kwargs(self):
r = self.app_.post_json('/multiple_optional/1', {'one': 'one'})
assert r.status_int == 200
assert r.body == b_('multiple_optional: 1, None, None')
def test_multiple_optional_encoded_positional_args_and_dict_kwargs(self): def test_multiple_optional_encoded_positional_args_and_dict_kwargs(self):
r = self.app_.post('/multiple_optional/One%21', {'one': 'one'}) r = self.app_.post('/multiple_optional/One%21', {'one': 'one'})
assert r.status_int == 200 assert r.status_int == 200
@@ -656,6 +701,14 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('multiple_optional: 1, 2, 3') assert r.body == b_('multiple_optional: 1, 2, 3')
def test_multiple_optional_args_with_multiple_json_kwargs(self):
r = self.app_.post_json(
'/multiple_optional',
{'one': '1', 'two': '2', 'three': '3', 'four': '4'}
)
assert r.status_int == 200
assert r.body == b_('multiple_optional: 1, 2, 3')
def test_multiple_optional_args_with_multiple_encoded_dict_kwargs(self): def test_multiple_optional_args_with_multiple_encoded_dict_kwargs(self):
r = self.app_.post( r = self.app_.post(
'/multiple_optional', '/multiple_optional',
@@ -709,6 +762,14 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('variable_args: ') assert r.body == b_('variable_args: ')
def test_variable_args_with_json_kwargs(self):
r = self.app_.post_json(
'/variable_args',
{'id': '3', 'dummy': 'dummy'}
)
assert r.status_int == 200
assert r.body == b_('variable_args: ')
def test_variable_kwargs(self): def test_variable_kwargs(self):
r = self.app_.get('/variable_kwargs') r = self.app_.get('/variable_kwargs')
assert r.status_int == 200 assert r.status_int == 200
@@ -735,6 +796,14 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('variable_kwargs: dummy=dummy, id=3') assert r.body == b_('variable_kwargs: dummy=dummy, id=3')
def test_multiple_variable_kwargs_with_json_kwargs(self):
r = self.app_.post_json(
'/variable_kwargs',
{'id': '3', 'dummy': 'dummy'}
)
assert r.status_int == 200
assert r.body == b_('variable_kwargs: dummy=dummy, id=3')
def test_multiple_variable_kwargs_with_encoded_dict_kwargs(self): def test_multiple_variable_kwargs_with_encoded_dict_kwargs(self):
r = self.app_.post( r = self.app_.post(
'/variable_kwargs', '/variable_kwargs',
@@ -779,6 +848,14 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('variable_all: 6, day=12, month=1') assert r.body == b_('variable_all: 6, day=12, month=1')
def test_variable_post_with_json_kwargs(self):
r = self.app_.post_json(
'/variable_all/6',
{'month': '1', 'day': '12'}
)
assert r.status_int == 200
assert r.body == b_('variable_all: 6, day=12, month=1')
def test_variable_post_mixed(self): def test_variable_post_mixed(self):
r = self.app_.post( r = self.app_.post(
'/variable_all/7', '/variable_all/7',
@@ -787,6 +864,14 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('variable_all: 7, day=12, id=seven, month=1') assert r.body == b_('variable_all: 7, day=12, id=seven, month=1')
def test_variable_post_mixed_with_json(self):
r = self.app_.post_json(
'/variable_all/7',
{'id': 'seven', 'month': '1', 'day': '12'}
)
assert r.status_int == 200
assert r.body == b_('variable_all: 7, day=12, id=seven, month=1')
def test_no_remainder(self): def test_no_remainder(self):
try: try:
r = self.app_.get('/eater') r = self.app_.get('/eater')
@@ -843,6 +928,11 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('eater: 9, None, day=12, month=1') assert r.body == b_('eater: 9, None, day=12, month=1')
def test_post_remainder_with_json_kwargs(self):
r = self.app_.post_json('/eater/9', {'month': '1', 'day': '12'})
assert r.status_int == 200
assert r.body == b_('eater: 9, None, day=12, month=1')
def test_post_many_remainders_with_many_kwargs(self): def test_post_many_remainders_with_many_kwargs(self):
r = self.app_.post( r = self.app_.post(
'/eater/10', '/eater/10',
@@ -851,6 +941,14 @@ class TestControllerArguments(PecanTestCase):
assert r.status_int == 200 assert r.status_int == 200
assert r.body == b_('eater: 10, dummy, day=12, month=1') assert r.body == b_('eater: 10, dummy, day=12, month=1')
def test_post_many_remainders_with_many_json_kwargs(self):
r = self.app_.post_json(
'/eater/10',
{'id': 'ten', 'month': '1', 'day': '12', 'dummy': 'dummy'}
)
assert r.status_int == 200
assert r.body == b_('eater: 10, dummy, day=12, month=1')
class TestDefaultErrorRendering(PecanTestCase): class TestDefaultErrorRendering(PecanTestCase):