Python requests.post方法中的data与json参数差异

遇到问题

在用requests.post方法测试接口调用时,使用data参数得到Response 400结果;使用json参数得到Response 200。测试代码如下。

import requests
import json

url = "https://10.20.32.92:8834/scans"
accesskey = 'dc9ada34d59'
secretkey = 'f3b404a433fea25'

header = {
    'X-ApiKeys': 'accessKey={accesskey};secretKey={secretkey}'.format(accesskey=accesskey, secretkey=secretkey),
    'X-API-Token':'0FADA7F5-8903-4686-81F7-ECD0096628EA',
    'Content-type': 'application/json',
    'Accept': 'text/plain'
}

data = {
    "uuid":"bbd4f805-3966-d464-b2d1-0079e2bcf",
    "settings":{
        "name":"testscan",
        "enabled": "true",
        "text_targets": "ip_list",
        "agent_group_id": []
    }
}


print(requests.post(url, headers=header, data=data, verify=False))
print(requests.post(url, headers=header, json=data, verify=False))

400与200含义

  • 400 Bad Request --- The server cannot or will not process the request due to an apparent client error (e.g., malformed request syntax, size too large, invalid request message framing, or deceptive request routing).
  • 200 OK --- Standard response for successful HTTP requests. The actual response will depend on the request method used. In a GET request, the response will contain an entity corresponding to the requested resource. In a POST request, the response will contain an entity describing or containing the result of the action.

data与json区别

从requests的官方文档解释中看出:

  • data用于发送form-encoded数据,类似HTML表单
  • json用于发送not form-encoded数据,例如发送字符串。
  • json参数将Content-Type改为application/json
  • json参数自动对传入的参数调用json.dumps()

从requests源码中的prepare_body函数中可看出针对json参数,修改了Content-Type以及调用json.dumps()的操作。

这样可能不能很好的理解这两个参数的区别,下面使用https://httpbin.org/post测试,代码如下:

import requests
import json

header = {
    'Content-type': 'application/json'
}
data = {
    "uuid":"test-uuid",
    "settings":{
        "name":"test-httpbin",
        "enabled": "true",
        "text_targets": "ip_list",
    }
}

print('\n')
print('-- [ data = data ] -------------------------------------------')
r = requests.post("https://httpbin.org/post", data=data)
print(r.text)


print('\n')
print('-- [ data = json.dumps ] -------------------------------------------')
r = requests.post("https://httpbin.org/post", data=json.dumps(data))
print(r.text)

print('\n')
print('-- [ json = data ] -------------------------------------------')
r = requests.post("https://httpbin.org/post", json=data)
print(r.text)

执行结果如下:

-- [ data = data ] -------------------------------------------
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "settings": [
      "name",
      "enabled",
      "text_targets"
    ],
    "uuid": "test-uuid"
  },
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "67",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.22.0"
  },
  "json": null,
  "url": "https://httpbin.org/post"
}

-- [ data = json.dumps ] -------------------------------------------
{
  "args": {},
  "data": "{\"uuid\": \"test-uuid\", \"settings\": {\"name\": \"test-httpbin\", \"enabled\": \"true\", \"text_targets\": \"ip_list\"}}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "105",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.22.0"
  },
  "json": {
    "settings": {
      "enabled": "true",
      "name": "test-httpbin",
      "text_targets": "ip_list"
    },
    "uuid": "test-uuid"
  },
  "url": "https://httpbin.org/post"
}

-- [ json = data ] -------------------------------------------
{
  "args": {},
  "data": "{\"uuid\": \"test-uuid\", \"settings\": {\"name\": \"test-httpbin\", \"enabled\": \"true\", \"text_targets\": \"ip_list\"}}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "105",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "python-requests/2.22.0"
  },
  "json": {
    "settings": {
      "enabled": "true",
      "name": "test-httpbin",
      "text_targets": "ip_list"
    },
    "uuid": "test-uuid"
  },
  "url": "https://httpbin.org/post"
}

从结果可看出data=json.dumps()json=data是等价的。而它们与data=data的差异在于数据存放的字段不同。

  • data=data,数据存放在form字段中
  • json=data,数据存放在datajson字段中

由此可看出,使用requests.post方法中的data或json参数发送数据时,发送的数据被放到不同的键(form或data/json)下。具体从哪个键取数据,这就看接口的实现方式了。因此,若接口从data/json取数据,而调用requests.post方法时使用data参数发送数据,则会导致接口取出的数据为空,从而返回400状态码。

补充1:HTTP状态码分类

HTTP响应状态代码分为五个类别。第一位表示响应的类别,后两位没有分类作用。五个分类如下:

  • 1xx informational response – the request was received, continuing process
  • 2xx successful – the request was successfully received, understood and accepted
  • 3xx redirection – further action needs to be taken in order to complete the request
  • 4xx client error – the request contains bad syntax or cannot be fulfilled
  • 5xx server error – the server failed to fulfill an apparently valid request

补充2:python字典和Json的区别

从字符串的表示规则上看,Python字典和JSON非常相似。但是Python字典本身是一个完整的数据结构,可实现其自己的所有算法。

  • json的key只能是字符串;Python字典的key可以是任何可hash对象
  • json的key可以是有序、重复的;Python字典的key不可以重复
  • json的key存在默认值undefined;Python字典key默认没有默认值
  • json的value只能是字符串、浮点数、布尔值或者null,或者它们构成的数组或者对象
  • json访问方式可以是[],也可以是.,遍历方式分in、of;dict的value仅可以下标访问
  • json的字符串强制双引号;Python字典的字符串可以单引号、双引号
  • json里只有数组;Python字典可以嵌套tuple
  • json:true、false、null;python:True、False、None
  • json中文必须是unicode编码,如"\u6211"
  • json的类型是字符串;Python字典的类型是字典

你可能感兴趣的:(Python requests.post方法中的data与json参数差异)