【问题标题】:Making a POST request using urllib with multiple headers gives 400 Bad Request error使用具有多个标头的 urllib 发出 POST 请求会出现 400 Bad Request 错误
【发布时间】:2018-11-27 12:53:01
【问题描述】:

我使用过requests 库,我知道如何使用它,但我只需要使用标准库,所以如果您不鼓励我使用requests,我将不胜感激。

我创建了一个处理 POST 请求的烧瓶服务器,然后从另一个脚本调用 urllib 对烧瓶服务器进行 POST 调用。 我需要在正文中发送一个原始 json,就像我们在 Postman 中一样。

Flask 服务器

from flask import Flask, request

app = Flask(__name__)
@app.route('/random', methods=['POST'])
def random():
    if request.method == 'POST':
        if request.headers.get('Authorization') and request.headers.get('Content-Type') == 'application/json':
            print(request.get_json())
            return "Success"
        else:
            print(request.get_json())
            return "Bad request"

app.run(host='0.0.0.0', port=5000, debug=True)

Urllib 客户端(另存为 test.py)-

import urllib.request
import urllib.parse
d = {"spam": 1, "eggs": 2, "bacon": 0}
data = urllib.parse.urlencode(d)
data = data.encode()
req = urllib.request.Request("http://localhost:5000/random", data)
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', 12345)
with urllib.request.urlopen(req) as f:
    print(f.read().decode('utf-8'))

只有 Authorization 标头我得到 Bad Request 作为输出,并且 json 在烧瓶服务器端是 None如预期的那样

只有 Content-Type 标头或两个标头我都会收到此错误 -

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    with urllib.request.urlopen(req) as f:
  File "C:\ProgramData\Anaconda3\lib\urllib\request.py", line 223, in urlopen
    return opener.open(url, data, timeout)
  File "C:\ProgramData\Anaconda3\lib\urllib\request.py", line 532, in open
    response = meth(req, response)
  File "C:\ProgramData\Anaconda3\lib\urllib\request.py", line 642, in http_response
    'http', request, response, code, msg, hdrs)
  File "C:\ProgramData\Anaconda3\lib\urllib\request.py", line 570, in error
    return self._call_chain(*args)
  File "C:\ProgramData\Anaconda3\lib\urllib\request.py", line 504, in _call_chain
    result = func(*args)
  File "C:\ProgramData\Anaconda3\lib\urllib\request.py", line 650, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 400: BAD REQUEST

整个事情很简单,但我无法理解为什么会发生这种情况,并且错误消息也没有太大帮助。

【问题讨论】:

    标签: python python-3.x post http-headers urllib


    【解决方案1】:

    服务器在request.get_json() 中出现故障。只有当客户端发送两个标头时才会发生这种情况,因为那是它到达这一行的时候。

    要修复它,请将客户端更改为以 JSON 格式发送数据:

    import json            # <-- Import json
    import urllib.request
    import urllib.parse
    
    d = {"spam": 1, "eggs": 2, "bacon": 0}
    data = json.dumps(d)   # <-- Dump the dictionary as JSON
    data = data.encode()
    req = urllib.request.Request("http://localhost:5000/random", data)
    req.add_header('Content-Type', 'application/json')
    req.add_header('Authorization', 12345)
    with urllib.request.urlopen(req) as f:
        print(f.read().decode('utf-8'))
    

    希望对你有帮助

    【讨论】:

    • 这确实解决了问题,但是我无法理解为什么我需要使用json.dumps 作为docsLegacy interface 之前的最后一个示例的第三个 ) 和其他一些 SO 帖子建议使用 urlencode 就像我有 d = {"spam": 1, "eggs": 2, "bacon": 0} data = urllib.parse.urlencode(d) data = data.encode()
    • 我所说的 SO 帖子是 thisthis
    • 您评论中的第一个链接没有解决问题。 accepted answer 使用 requests 库,它也适合你,我推荐它。您评论中的第二个链接适用于urlencode,因为它正在模拟发布表单。 POST 请求中的数据只是一个大字符串。编码告诉服务器如何解释该数据。尝试打印原始请求 (print request.data) 而不是使用 get_json() 来查看 URL 编码数据和 JSON 之间的区别。
    • 我现在终于明白了。非常感谢您的时间和精力。但是我正在玩你的建议,我发现了这个奇怪的东西,如果我没有从 urllib 脚本传递任何标题,但我确实传递了 json,那么服务器会收到一个空字节,如 b'' 。但是,当我通过 POSTMAN 执行相同操作时,即通过取消勾选标题来手动禁用标题但保持 json 正文原样,我仍然以某种方式将整个 json 字节作为b'{"spam": 1, "eggs": 2, "bacon": 0}'。知道为什么会这样吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 2019-06-01
    • 2021-08-29
    • 1970-01-01
    • 1970-01-01
    • 2012-12-29
    相关资源
    最近更新 更多