Adds json parameter for POST requests
authorCarol Willing <carolcode@willingconsulting.com>
Thu, 17 Jul 2014 19:34:31 +0000 (12:34 -0700)
committerCarol Willing <carolcode@willingconsulting.com>
Thu, 28 Aug 2014 19:53:19 +0000 (12:53 -0700)
requests/api.py
requests/models.py
requests/sessions.py
test_requests.py

index 01d853d5cae6fd270027f19407eae3d266fd38d7..88db7dc72ec8e876b84ec2a8886f37a0e0edbb18 100644 (file)
@@ -22,6 +22,7 @@ def request(method, url, **kwargs):
     :param url: URL for the new :class:`Request` object.
     :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
     :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
+    :param json: (optional) json data to send in the body of the :class:`Request`.
     :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
     :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
     :param files: (optional) Dictionary of 'name': file-like-objects (or {'name': ('filename', fileobj)}) for multipart encoding upload.
@@ -77,15 +78,16 @@ def head(url, **kwargs):
     return request('head', url, **kwargs)
 
 
-def post(url, data=None, **kwargs):
+def post(url, data=None, json=None, **kwargs):
     """Sends a POST request. Returns :class:`Response` object.
 
     :param url: URL for the new :class:`Request` object.
     :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
+    :param json: (optional) json data to send in the body of the :class:`Request`.
     :param \*\*kwargs: Optional arguments that ``request`` takes.
     """
 
-    return request('post', url, data=data, **kwargs)
+    return request('post', url, data=data, json=json, **kwargs)
 
 
 def put(url, data=None, **kwargs):
index 03ff627adffa009306b77c64488aeb045a882404..b6ef9190077e1415f0c9816e68dbcda1179c3f3a 100644 (file)
@@ -190,6 +190,7 @@ class Request(RequestHooksMixin):
     :param headers: dictionary of headers to send.
     :param files: dictionary of {filename: fileobject} files to multipart upload.
     :param data: the body to attach the request. If a dictionary is provided, form-encoding will take place.
+    :param json: json for the body to attach the request.
     :param params: dictionary of URL parameters to append to the URL.
     :param auth: Auth handler or (user, pass) tuple.
     :param cookies: dictionary or CookieJar of cookies to attach to this request.
@@ -209,6 +210,7 @@ class Request(RequestHooksMixin):
         headers=None,
         files=None,
         data=None,
+        json=None,
         params=None,
         auth=None,
         cookies=None,
@@ -216,6 +218,7 @@ class Request(RequestHooksMixin):
 
         # Default empty dicts for dict params.
         data = [] if data is None else data
+        json = [] if json is None else json
         files = [] if files is None else files
         headers = {} if headers is None else headers
         params = {} if params is None else params
@@ -230,6 +233,7 @@ class Request(RequestHooksMixin):
         self.headers = headers
         self.files = files
         self.data = data
+        self.json = json
         self.params = params
         self.auth = auth
         self.cookies = cookies
@@ -246,6 +250,7 @@ class Request(RequestHooksMixin):
             headers=self.headers,
             files=self.files,
             data=self.data,
+            json=self.json,
             params=self.params,
             auth=self.auth,
             cookies=self.cookies,
@@ -289,7 +294,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
         self.hooks = default_hooks()
 
     def prepare(self, method=None, url=None, headers=None, files=None,
-                data=None, params=None, auth=None, cookies=None, hooks=None):
+                data=None, json=None, params=None, auth=None, cookies=None, hooks=None):
         """Prepares the entire request with the given parameters."""
 
         self.prepare_method(method)
@@ -397,7 +402,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
         else:
             self.headers = CaseInsensitiveDict()
 
-    def prepare_body(self, data, files):
+    def prepare_body(self, data, files, _json=None):
         """Prepares the given HTTP body data."""
 
         # Check if file, fo, generator, iterator.
@@ -408,6 +413,10 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
         content_type = None
         length = None
 
+        if _json is not None:
+            content_type = 'application/json'
+            data = json.dumps(_json)
+
         is_stream = all([
             hasattr(data, '__iter__'),
             not isinstance(data, (basestring, list, tuple, dict))
@@ -435,10 +444,11 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
             else:
                 if data:
                     body = self._encode_params(data)
-                    if isinstance(data, basestring) or hasattr(data, 'read'):
-                        content_type = None
-                    else:
-                        content_type = 'application/x-www-form-urlencoded'
+                    if not _json:
+                        if isinstance(data, basestring) or hasattr(data, 'read'):
+                            content_type = None
+                        else:
+                            content_type = 'application/x-www-form-urlencoded'
 
             self.prepare_content_length(body)
 
index 508b0ef29af4514db4383e6dc36a9bf56cf27c3e..7942447fd5a1ac8b27db43b1de1c6ba65ef96315 100644 (file)
@@ -365,6 +365,7 @@ class Session(SessionRedirectMixin):
             url=request.url,
             files=request.files,
             data=request.data,
+            json=request.json,
             headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict),
             params=merge_setting(request.params, self.params),
             auth=merge_setting(auth, self.auth),
@@ -376,6 +377,7 @@ class Session(SessionRedirectMixin):
     def request(self, method, url,
         params=None,
         data=None,
+        json=None,
         headers=None,
         cookies=None,
         files=None,
@@ -396,6 +398,8 @@ class Session(SessionRedirectMixin):
             string for the :class:`Request`.
         :param data: (optional) Dictionary or bytes to send in the body of the
             :class:`Request`.
+        :param json: (optional) json to send in the body of the
+            :class:`Request`.
         :param headers: (optional) Dictionary of HTTP Headers to send with the
             :class:`Request`.
         :param cookies: (optional) Dict or CookieJar object to send with the
@@ -426,6 +430,7 @@ class Session(SessionRedirectMixin):
             headers = headers,
             files = files,
             data = data or {},
+            json = json or {},
             params = params or {},
             auth = auth,
             cookies = cookies,
@@ -479,15 +484,16 @@ class Session(SessionRedirectMixin):
         kwargs.setdefault('allow_redirects', False)
         return self.request('HEAD', url, **kwargs)
 
-    def post(self, url, data=None, **kwargs):
+    def post(self, url, data=None, json=None, **kwargs):
         """Sends a POST request. Returns :class:`Response` object.
 
         :param url: URL for the new :class:`Request` object.
         :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
+        :param json: (optional) json to send in the body of the :class:`Request`.
         :param \*\*kwargs: Optional arguments that ``request`` takes.
         """
 
-        return self.request('POST', url, data=data, **kwargs)
+        return self.request('POST', url, data=data, json=json, **kwargs)
 
     def put(self, url, data=None, **kwargs):
         """Sends a PUT request. Returns :class:`Response` object.
index 716c0dcff6f7c97d6786b898f08cbdefb17050e1..2e98cb91f3c2ef8ef94d1a4c493b9f150107956b 100755 (executable)
@@ -986,6 +986,14 @@ class RequestsTestCase(unittest.TestCase):
             assert item.history == total[0:i]
             i=i+1
 
+    def test_json_param_post_content_type_works(self):
+        r = requests.post(
+            httpbin('post'),
+            json={'life': 42}
+        )
+        assert r.status_code == 200
+        assert 'application/json' in r.headers['Content-Type']
+
 
 class TestContentEncodingDetection(unittest.TestCase):