为什么指定JSON并在指定数据时python请求。

发布于 2025-02-09 00:30:55 字数 1407 浏览 2 评论 0原文

我正在使用请求库发送http请求并找到以下行为奇怪:当我使用json =来构建请求的身体时,其类型变为bytes,当我使用data =构建请求的主体时,其类型保留str。是否有原因为什么使用JSON =会引导请求库开发人员对身体右边的范围进行编码?

In [8]: requests.Request('GET', 'https://httpbin.org/get', json={'a': 5}).prepare().body
Out[8]: b'{"a": 5}' # bytes

In [9]: requests.Request('GET', 'https://httpbin.org/get', data=json.dumps({'a': 5})).prepare().body
Out[9]: '{"a": 5}'  # str

来自requests.models

    def prepare_body(self, data, files, json=None):
        """Prepares the given HTTP body data."""

        # Check if file, fo, generator, iterator.
        # If not, run through normal process.

        # Nottin' on you.
        body = None
        content_type = None

        if not data and json is not None:
            # urllib3 requires a bytes-like body. Python 2's json.dumps
            # provides this natively, but Python 3 gives a Unicode string.
            content_type = 'application/json'

            try:
              body = complexjson.dumps(json, allow_nan=False)
            except ValueError as ve:
              raise InvalidJSONError(ve, request=self)

            if not isinstance(body, bytes):
                body = body.encode('utf-8') # !!! ACTIVELY ENCODED HERE !!!

I am using requests library to send HTTP requests and found the following behavior bizarre: when I use json= to build request's body, its type becomes bytes, and when I use data= to build request's body, its type remains str. Is there a reason why using json= would lead requests library developers to encode the body right-away?

In [8]: requests.Request('GET', 'https://httpbin.org/get', json={'a': 5}).prepare().body
Out[8]: b'{"a": 5}' # bytes

In [9]: requests.Request('GET', 'https://httpbin.org/get', data=json.dumps({'a': 5})).prepare().body
Out[9]: '{"a": 5}'  # str

From requests.models:

    def prepare_body(self, data, files, json=None):
        """Prepares the given HTTP body data."""

        # Check if file, fo, generator, iterator.
        # If not, run through normal process.

        # Nottin' on you.
        body = None
        content_type = None

        if not data and json is not None:
            # urllib3 requires a bytes-like body. Python 2's json.dumps
            # provides this natively, but Python 3 gives a Unicode string.
            content_type = 'application/json'

            try:
              body = complexjson.dumps(json, allow_nan=False)
            except ValueError as ve:
              raise InvalidJSONError(ve, request=self)

            if not isinstance(body, bytes):
                body = body.encode('utf-8') # !!! ACTIVELY ENCODED HERE !!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

他是夢罘是命 2025-02-16 00:30:55

尝试此代码以行使许多可能的请求配置,并注意与每个结果准备好的Quect创建的车身类型和标头的差异:

from requests import Request

url = 'http://127.0.0.1:8000/upload_image/'
# tiny base64 encoded image as a data url
data_url = 'data:image/png;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs='
post_json = {
    'image': data_url
}
examples = {
    'data': Request('POST', url,
        data=post_json).prepare(),
    'json': Request('POST', url,
        json=post_json).prepare(),
    'data_and_json': Request('POST', url,
        data=post_json,
        json=post_json
    ).prepare(),
}
# when data and json are both included requests uses data and ignores json

# use the formatted string output from the prepared data example
examples['data_str'] = Request('POST', url,
        data=examples['data'].body).prepare()

# use the formatted bytes output from the prepared json example
examples['json_bytes'] = Request('POST', url,
        data=examples['json'].body).prepare()

for i in examples:
    print(i, type(examples[i].body), examples[i].headers)
# note the type of the body and the headers created
#### OUTPUT:

data <class 'str'> {'Content-Length': '92', 'Content-Type': 'application/x-www-form-urlencoded'}
json <class 'bytes'> {'Content-Length': '83', 'Content-Type': 'application/json'}
data_and_json <class 'str'> {'Content-Length': '92', 'Content-Type': 'application/x-www-form-urlencoded'}
data_str <class 'str'> {'Content-Length': '92'}
json_bytes <class 'bytes'> {'Content-Length': '83'}

要注意的一件事是,如果您同时填充数据和JSON参数参数,请求请求默认情况下使用数据并忽略JSON 。

我没有调查为身体提供流。

在库的源文件中查看prepar_body,以查看这是如何发生的:

https://github.com/psf/requests/blob/88dce9d854797c05d0ff296b70e0430535353535355353553555555555.Src/src/requests/requests/models.py#l494

    def prepare_body(self, data, files, json=None):
        """Prepares the given HTTP body data."""

        # Check if file, fo, generator, iterator.
        # If not, run through normal process.

        # Nottin' on you.
        body = None
        content_type = None

        if not data and json is not None:
            # urllib3 requires a bytes-like body. Python 2's json.dumps
            # provides this natively, but Python 3 gives a Unicode string.
            content_type = "application/json"

            try:
                body = complexjson.dumps(json, allow_nan=False)
            except ValueError as ve:
                raise InvalidJSONError(ve, request=self)

            if not isinstance(body, bytes):
                body = body.encode("utf-8")

        is_stream = all(
            [
                hasattr(data, "__iter__"),
                not isinstance(data, (basestring, list, tuple, Mapping)),
            ]
        )

        if is_stream:
            try:
                length = super_len(data)
            except (TypeError, AttributeError, UnsupportedOperation):
                length = None

            body = data

            if getattr(body, "tell", None) is not None:
                # Record the current file position before reading.
                # This will allow us to rewind a file in the event
                # of a redirect.
                try:
                    self._body_position = body.tell()
                except OSError:
                    # This differentiates from None, allowing us to catch
                    # a failed `tell()` later when trying to rewind the body
                    self._body_position = object()

            if files:
                raise NotImplementedError(
                    "Streamed bodies and files are mutually exclusive."
                )

            if length:
                self.headers["Content-Length"] = builtin_str(length)
            else:
                self.headers["Transfer-Encoding"] = "chunked"
        else:
            # Multi-part file uploads.
            if files:
                (body, content_type) = self._encode_files(files, data)
            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"

            self.prepare_content_length(body)

            # Add content-type if it wasn't explicitly provided.
            if content_type and ("content-type" not in self.headers):
                self.headers["Content-Type"] = content_type

        self.body = body

如果我也开始探索发送功能,以查看我的事件在发送请求之前,但我还没有弄清楚那部分

try this code to exercise a number of possible Request configurations and note the differences in the body type and headers created with each resulting PreparedRequest:

from requests import Request

url = 'http://127.0.0.1:8000/upload_image/'
# tiny base64 encoded image as a data url
data_url = 'data:image/png;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs='
post_json = {
    'image': data_url
}
examples = {
    'data': Request('POST', url,
        data=post_json).prepare(),
    'json': Request('POST', url,
        json=post_json).prepare(),
    'data_and_json': Request('POST', url,
        data=post_json,
        json=post_json
    ).prepare(),
}
# when data and json are both included requests uses data and ignores json

# use the formatted string output from the prepared data example
examples['data_str'] = Request('POST', url,
        data=examples['data'].body).prepare()

# use the formatted bytes output from the prepared json example
examples['json_bytes'] = Request('POST', url,
        data=examples['json'].body).prepare()

for i in examples:
    print(i, type(examples[i].body), examples[i].headers)
# note the type of the body and the headers created
#### OUTPUT:

data <class 'str'> {'Content-Length': '92', 'Content-Type': 'application/x-www-form-urlencoded'}
json <class 'bytes'> {'Content-Length': '83', 'Content-Type': 'application/json'}
data_and_json <class 'str'> {'Content-Length': '92', 'Content-Type': 'application/x-www-form-urlencoded'}
data_str <class 'str'> {'Content-Length': '92'}
json_bytes <class 'bytes'> {'Content-Length': '83'}

One thing to note is that if you populate both data and json parameters requests will use data by default and ignore json.

I did not investigate providing a stream for the body.

Look for prepare_body in the library's source file to see how this happens:

https://github.com/psf/requests/blob/88dce9d854797c05d0ff296b70e0430535ef8aaf/src/requests/models.py#L494

    def prepare_body(self, data, files, json=None):
        """Prepares the given HTTP body data."""

        # Check if file, fo, generator, iterator.
        # If not, run through normal process.

        # Nottin' on you.
        body = None
        content_type = None

        if not data and json is not None:
            # urllib3 requires a bytes-like body. Python 2's json.dumps
            # provides this natively, but Python 3 gives a Unicode string.
            content_type = "application/json"

            try:
                body = complexjson.dumps(json, allow_nan=False)
            except ValueError as ve:
                raise InvalidJSONError(ve, request=self)

            if not isinstance(body, bytes):
                body = body.encode("utf-8")

        is_stream = all(
            [
                hasattr(data, "__iter__"),
                not isinstance(data, (basestring, list, tuple, Mapping)),
            ]
        )

        if is_stream:
            try:
                length = super_len(data)
            except (TypeError, AttributeError, UnsupportedOperation):
                length = None

            body = data

            if getattr(body, "tell", None) is not None:
                # Record the current file position before reading.
                # This will allow us to rewind a file in the event
                # of a redirect.
                try:
                    self._body_position = body.tell()
                except OSError:
                    # This differentiates from None, allowing us to catch
                    # a failed `tell()` later when trying to rewind the body
                    self._body_position = object()

            if files:
                raise NotImplementedError(
                    "Streamed bodies and files are mutually exclusive."
                )

            if length:
                self.headers["Content-Length"] = builtin_str(length)
            else:
                self.headers["Transfer-Encoding"] = "chunked"
        else:
            # Multi-part file uploads.
            if files:
                (body, content_type) = self._encode_files(files, data)
            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"

            self.prepare_content_length(body)

            # Add content-type if it wasn't explicitly provided.
            if content_type and ("content-type" not in self.headers):
                self.headers["Content-Type"] = content_type

        self.body = body

i also started looking into the send function to see if i could figure out what further conversions occur before the request is sent, but i have not figured that part out yet

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文