From 921a8b77dcb11d4e3668bf23a5731b665b748a92 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Tue, 23 Sep 2014 10:35:37 -0400 Subject: For HTTP POSTs, map JSON request bodies to controller keyword arguments. Fixes-bug: 1336943 Change-Id: I2e59e5d43d87a5279c41b155188ebe3281de0e11 --- pecan/core.py | 9 +++++ pecan/tests/test_base.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/pecan/core.py b/pecan/core.py index fc0468e..934931a 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -14,6 +14,7 @@ import six from webob import (Request as WebObRequest, Response as WebObResponse, exc, acceptparse) +from webob.multidict import NestedMultiDict from .compat import urlparse, unquote_plus, izip from .secure import handle_security @@ -499,6 +500,14 @@ class PecanBase(object): # fetch any parameters if req.method == '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: params = dict(req.params) diff --git a/pecan/tests/test_base.py b/pecan/tests/test_base.py index e6ddb05..b22b2c8 100644 --- a/pecan/tests/test_base.py +++ b/pecan/tests/test_base.py @@ -440,6 +440,16 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.get('/?id=5&dummy=dummy') assert r.status_int == 200 @@ -450,6 +460,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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 def test_multiple_positional_arguments(self): @@ -477,6 +492,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post('/multiple', {'one': 'Five%20', 'two': 'Six%20%21'}) assert r.status_int == 200 @@ -527,6 +547,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post('/optional', {'id': 'Some%20Number'}) assert r.status_int == 200 @@ -537,6 +562,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post('/optional/Some%20Number', {'id': 'five'}) assert r.status_int == 200 @@ -557,6 +587,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post('/optional', { 'id': 'Some%20Number', @@ -621,6 +656,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post('/multiple_optional', {'one': 'One%21'}) assert r.status_int == 200 @@ -631,6 +671,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post('/multiple_optional/One%21', {'one': 'one'}) assert r.status_int == 200 @@ -656,6 +701,14 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post( '/multiple_optional', @@ -709,6 +762,14 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.get('/variable_kwargs') assert r.status_int == 200 @@ -735,6 +796,14 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post( '/variable_kwargs', @@ -779,6 +848,14 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post( '/variable_all/7', @@ -787,6 +864,14 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): try: r = self.app_.get('/eater') @@ -843,6 +928,11 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): r = self.app_.post( '/eater/10', @@ -851,6 +941,14 @@ class TestControllerArguments(PecanTestCase): assert r.status_int == 200 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): -- cgit v1.2.1