You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改Tornado代码以实现成功POST请求?

问题:Tornado AsyncHTTPClient调用接口返回Bad Request如何解决?

我用requests库调用接口能成功运行,代码如下:

import requests
from pprint import pprint
import json

PEM = '/full/path/to/my.pem'

client_id='cliendID'
client_secret='clientSecret'

USER='myuser'
PSWD='mypwd'

url = 'https://theurl.com/request/'
 
data = {
    "grant_type": "password",
    "username": USER,
    "password": PSWD
}

auth = (client_id, client_secret)
response = requests.post( url, auth=auth, data=data, verify=PEM )
answer = response.json()['answer']

print( answer )

打印结果符合预期。

用curl调用该接口同样成功:

curl -iv --user cliendID:clientSecret --key /full/path/to/server.key --cert /full/path/to/my.pem -F grant_type=password -F username=myuser -F password=mypwd https://theurl.com/request/

但用Tornado的AsyncHTTPClient实现相同功能时,收到“Bad Request”响应,示例代码如下:

from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
import json

async def get_content():
    PEM = '/full/path/to/my.pem'

    client_id='cliendID'
    client_secret='clientSecret'

    url = 'https://theurl.com/request/'

    USER='myuser'
    PSWD='mypwd'

    data = {
        "grant_type": "password",
        "username": USER,
        "password": PSWD
    }


    bodyData = json.dumps( data )

    http_client = AsyncHTTPClient()
    response = await http_client.fetch( url,
                                        method        = 'POST',
                                        body          = bodyData,
                                        auth_username = client_id,
                                        auth_password = client_secret,
                                        ca_certs      = PEM )

    print( response.body.decode() )



async def main():
    await get_content()



if __name__ == "__main__":
    io_loop = IOLoop.current()
    io_loop.run_sync(main)

我猜测问题出在bodyData的传递方式上,请问需修改Tornado代码的哪些部分才能让请求成功?


解决办法

你的猜测正确,问题确实出在请求体的格式上。requests的data参数和curl的-F参数都是发送表单数据(application/x-www-form-urlencoded),但你用Tornado时把数据转成了JSON格式发送,接口无法识别,因此返回Bad Request。需要做以下修改:

  1. 替换请求体编码方式:不要用json.dumps,改用urllib.parse.urlencode将数据编码为表单格式
  2. 设置正确的请求头:添加Content-Type头,明确告诉服务器请求体是表单格式

修改后的Tornado代码如下:

from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
from urllib.parse import urlencode

async def get_content():
    PEM = '/full/path/to/my.pem'

    client_id='cliendID'
    client_secret='clientSecret'

    url = 'https://theurl.com/request/'

    USER='myuser'
    PSWD='mypwd'

    data = {
        "grant_type": "password",
        "username": USER,
        "password": PSWD
    }

    # 将数据编码为表单格式
    bodyData = urlencode(data)

    http_client = AsyncHTTPClient()
    response = await http_client.fetch( url,
                                        method        = 'POST',
                                        body          = bodyData,
                                        auth_username = client_id,
                                        auth_password = client_secret,
                                        ca_certs      = PEM,
                                        # 添加Content-Type请求头
                                        headers       = {"Content-Type": "application/x-www-form-urlencoded"} )

    print( response.body.decode() )

async def main():
    await get_content()

if __name__ == "__main__":
    io_loop = IOLoop.current()
    io_loop.run_sync(main)

额外注意:
如果你的curl命令中的--key和--cert是用于双向认证的客户端证书,而当前Tornado代码仅设置了ca_certs(验证服务器证书),若接口要求客户端证书,还需补充添加client_key和client_cert参数:

response = await http_client.fetch( url,
                                    # 其他参数保持不变
                                    client_key='/full/path/to/server.key',
                                    client_cert='/full/path/to/my.pem' )

不过从你的requests代码来看,仅设置了verify=PEM(仅验证服务器证书),未配置客户端证书,所以这一步可能不需要,除非接口实际要求双向认证。


内容的提问来源于stack exchange,提问作者Eric G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 23:01:30