Skip to content

Commit

Permalink
add json parameter to client request method #1726
Browse files Browse the repository at this point in the history
  • Loading branch information
fafhrd91 committed Mar 20, 2017
1 parent c283a46 commit 8376d4a
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 7 deletions.
7 changes: 6 additions & 1 deletion aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ def request(self, method, url, **kwargs):
def _request(self, method, url, *,
params=None,
data=None,
json=None,
headers=None,
skip_auto_headers=None,
auth=None,
Expand Down Expand Up @@ -160,6 +161,10 @@ def _request(self, method, url, *,
if self.closed:
raise RuntimeError('Session is closed')

if data is not None and json is not None:
raise ValueError(
'data and json parameters can not be used at the same time')

if not isinstance(chunked, bool) and chunked is not None:
warnings.warn(
'Chunk size is deprecated #1615', DeprecationWarning)
Expand Down Expand Up @@ -203,7 +208,7 @@ def _request(self, method, url, *,

req = self._request_class(
method, url, params=params, headers=headers,
skip_auto_headers=skip_headers, data=data,
skip_auto_headers=skip_headers, data=data, json=json,
cookies=cookies, auth=auth, version=version,
compress=compress, chunked=chunked,
expect100=expect100, loop=self._loop,
Expand Down
5 changes: 4 additions & 1 deletion aiohttp/client_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class ClientRequest:

def __init__(self, method, url, *,
params=None, headers=None, skip_auto_headers=frozenset(),
data=None, cookies=None,
data=None, json=None, cookies=None,
auth=None, version=http.HttpVersion11, compress=None,
chunked=None, expect100=False,
loop=None, response_class=None,
Expand Down Expand Up @@ -84,6 +84,9 @@ def __init__(self, method, url, *,
if loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1))

if json is not None:
data = payload.JsonPayload(json)

self.update_version(version)
self.update_host(url)
self.update_headers(headers)
Expand Down
14 changes: 13 additions & 1 deletion aiohttp/payload.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import io
import json
import mimetypes
import os
from abc import ABC, abstractmethod
Expand All @@ -14,7 +15,7 @@
__all__ = ('PAYLOAD_REGISTRY', 'get_payload', 'payload_type', 'Payload',
'BytesPayload', 'StringPayload', 'StreamReaderPayload',
'IOBasePayload', 'BytesIOPayload', 'BufferedReaderPayload',
'TextIOPayload', 'StringIOPayload')
'TextIOPayload', 'StringIOPayload', 'JsonPayload')


class LookupError(Exception):
Expand Down Expand Up @@ -284,6 +285,17 @@ def write(self, writer):
break


class JsonPayload(BytesPayload):

def __init__(self, value,
encoding='utf-8', content_type='application/json',
*args, **kwargs):

super().__init__(
json.dumps(value).encode(encoding),
content_type=content_type, encoding=encoding, *args, **kwargs)


PAYLOAD_REGISTRY = PayloadRegistry()
PAYLOAD_REGISTRY.register(BytesPayload, (bytes, bytearray, memoryview))
PAYLOAD_REGISTRY.register(StringPayload, str)
Expand Down
13 changes: 9 additions & 4 deletions docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ The client session supports the context manager protocol for self closing.

A read-only property.

.. comethod:: request(method, url, *, params=None, data=None,\
.. comethod:: request(method, url, *, params=None, data=None, json=None,\
headers=None, skip_auto_headers=None, \
auth=None, allow_redirects=True,\
max_redirects=10, version=HttpVersion(major=1, minor=1),\
Expand Down Expand Up @@ -180,6 +180,9 @@ The client session supports the context manager protocol for self closing.
:param data: Dictionary, bytes, or file-like object to
send in the body of the request (optional)

:param json: Any json compatible python object (optional). `json` and `data`
parameters could not be used at the same time.

:param dict headers: HTTP Headers to send with
the request (optional)

Expand Down Expand Up @@ -484,7 +487,7 @@ keepaliving, cookies and complex connection stuff like properly configured SSL
certification chaining.


.. coroutinefunction:: request(method, url, *, params=None, data=None, \
.. coroutinefunction:: request(method, url, *, params=None, data=None, json=None,\
headers=None, cookies=None, auth=None, \
allow_redirects=True, max_redirects=10, \
encoding='utf-8', \
Expand All @@ -506,8 +509,10 @@ certification chaining.
:param data: Dictionary, bytes, or file-like object to
send in the body of the request (optional)

:param dict headers: HTTP Headers to send with
the request (optional)
:param json: Any json compatible python object (optional). `json` and `data`
parameters could not be used at the same time.

:param dict headers: HTTP Headers to send with the request (optional)

:param dict cookies: Cookies to send with the request (optional)

Expand Down
22 changes: 22 additions & 0 deletions tests/test_client_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,28 @@ def handler(request):
resp.close()


@asyncio.coroutine
def test_json(loop, test_client):
@asyncio.coroutine
def handler(request):
assert request.content_type == 'application/json'
data = yield from request.json()
return web.Response(body=aiohttp.JsonPayload(data))

app = web.Application(loop=loop)
app.router.add_post('/', handler)
client = yield from test_client(app)

resp = yield from client.post('/', json={'some': 'data'})
assert 200 == resp.status
content = yield from resp.json()
assert content == {'some': 'data'}
resp.close()

with pytest.raises(ValueError):
yield from client.post('/', data="some data", json={'some': 'data'})


@asyncio.coroutine
def test_expect_continue(loop, test_client):
expect_called = False
Expand Down
8 changes: 8 additions & 0 deletions tests/test_client_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,14 @@ def test_chunked_length(loop, conn):
headers={'CONTENT-LENGTH': '1000'}, chunked=True, loop=loop)


@asyncio.coroutine
def test_chunked_transfer_encoding(loop, conn):
with pytest.raises(ValueError):
ClientRequest(
'get', URL('http://python.org/'),
headers={'TRANSFER-ENCODING': 'chunked'}, chunked=True, loop=loop)


@asyncio.coroutine
def test_file_upload_not_chunked(loop):
here = os.path.dirname(__file__)
Expand Down

0 comments on commit 8376d4a

Please sign in to comment.