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

Python Requests POST中data与json参数差异及Token接口调用问题

关于Requests库POST请求参数传递的问题解析

问题背景

之前以为对字典d来说,requests.post(url, data=json.dumps(d))和requests.post(url, json=d)是等价的,但调用某个Token接口时发现完全不是这么回事:
这个接口的文档定义如下:

  • URL:{{base_url}}/token
  • 参数:
    • grant_type={{password}}
    • username={{username}}
    • password={{password}}
    • scope={"account":"{{account}}", "tenant":"{{tenant}}"}

我从.env加载变量后试了三种传参方式,结果完全不同:

  1. 用json参数传完整字典,直接返回'{"error":"unsupported_grant_type"}'
resp = requests.post(f'{base_url}/token',
                     json={'grant_type': 'password', 'username': uname, 'password': pwd,
                           'scope': {'account': account, 'tenant': tenant}})
  1. 用data参数传完整字典,返回解析错误:'{"error":"invalid_grant","error_description":"{\\"ErrorMessage\\":\\"Error trying to Login - User [username] Account [] Unexpected character encountered while parsing value: a.'
resp = requests.post(f'{base_url}/token',
                     data={'grant_type': 'password', 'username': uname, 'password': pwd,
                           'scope': {'account': account, 'tenant': tenant}})
  1. 混合方式:data参数里只把scope的值用json.dumps序列化,请求成功了
resp = requests.post(f'{base_url}/token',
                 data={'grant_type': 'password', 'username': uname, 'password': pwd,
                       'scope': json.dumps({'account': account, 'tenant': tenant})})

问题解答

1. 这种混合使用的差异是否真实存在?为何与之前看到的“要么用data=json.dumps(d)要么用json=d”的结论不同?

差异是真实存在的,核心原因在于这个接口要的不是纯JSON,也不是纯表单键值对,而是「表单键值对里嵌套JSON字符串」的混合格式:

  • 用json=d时,Requests会把整个字典转成JSON字符串,同时设置请求头Content-Type: application/json发送,但这个接口可能只认application/x-www-form-urlencoded类型的表单请求,所以压根识别不到grant_type参数,直接返回unsupported_grant_type。
  • 用data=d时,Requests会把字典转成表单键值对,但对于scope里的嵌套字典,它只会直接转成普通字符串(比如{'account': 'xxx', 'tenant': 'yyy'}会变成"{'account': 'xxx', 'tenant': 'yyy'}"),这种带单引号的非标准JSON,接口解析时直接报错,返回invalid_grant。
  • 混合方式里,我们手动把scope转成标准JSON字符串,再放到表单键值对里发送,既符合接口要求的表单请求类型,又保证scope参数是能正常解析的标准格式,所以请求成功。

之前的结论只适用于「接口只接受纯JSON」或者「接口只接受纯表单键值对」的场景,碰到这种混合格式的接口就不适用了。

2. 作为API新手,能否从文档看出这种参数要求,还是只能通过试错发现?

完全可以从文档里看出来,重点盯这两点:

  • 先看接口的Content-Type要求:如果文档明确说要application/x-www-form-urlencoded,那就是表单提交,不是JSON提交。
  • 再看参数定义:文档里scope的示例是{"account":"{{account}}", "tenant":"{{tenant}}"},这里的大括号是JSON格式,说明scope的值得是一个JSON字符串,不是嵌套的表单参数。
    如果文档写得模糊,试错也是很正常的操作,毕竟不少接口文档都写得很随意。

3. 除了最终的解决方案,有没有更规范的参数传递方式?

有两种更严谨的方式:

  • 方式一:显式指定Content-Type
    虽然混合方式已经成功,但可以明确设置请求头的Content-Type为application/x-www-form-urlencoded,让请求意图更清晰:
    import requests
    import json
    
    payload = {
        'grant_type': 'password',
        'username': uname,
        'password': pwd,
        'scope': json.dumps({'account': account, 'tenant': tenant})
    }
    resp = requests.post(f'{base_url}/token', data=payload, headers={'Content-Type': 'application/x-www-form-urlencoded'})
    
  • 方式二:用urllib编码表单数据
    对于复杂的表单参数,可以用urllib.parse.urlencode来编码,确保参数格式完全符合表单规范:
    import requests
    import json
    from urllib.parse import urlencode
    
    payload = {
        'grant_type': 'password',
        'username': uname,
        'password': pwd,
        'scope': json.dumps({'account': account, 'tenant': tenant})
    }
    encoded_payload = urlencode(payload)
    resp = requests.post(f'{base_url}/token', data=encoded_payload, headers={'Content-Type': 'application/x-www-form-urlencoded'})
    

这两种方式都比直接传字典更稳妥,能避免Requests自动处理参数时可能出现的格式问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:23:19