将 curl 请求转换为 python 代码时请求错误

bad request while converting curl request as python code

我正在尝试将以下 curl 请求转换为 python@3.8 代码。 curl 向服务器发送 excel 文件数据。

curl 请求已正确解析,但我的 python 代码无法正常工作,我从服务器收到错误请求。

curl -X POST "http://localhost:8080/rest/2.0/import/ejob" -H "accept: application/json" -H "Authorization: Basic xyz" -H "Content-Type: multipart/form-data" -F "deleteFile=" -F "simulation=" -F "fileName=import_file" -F "sheetName=" -F "headerRow=" -F "sendNotification=" -F "sheetIndex=" -F "fileId=" -F 'template= [ { "resourceType": "xyz", "type": { "name": "xyz" }, "identifier": { "name": "", "domain": { "name": "xyz", "community": { "name": "test" } } } } ]' -F "batchSize=10000" -F "file=@filename.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

Python代码:

url = "http://localhost:8080/rest/2.0/import/ejob"

payload={'deleteFile': '',
'simulation': '',
'fileName': 'xyz',
'sheetName': '',
'headerRow': '',
'sendNotification': '',
'sheetIndex': '',
'fileId': '',
'template': ' [ { "resourceType": "Asset", "type": { "name": "xyz" }, "identifier": { "name": "", "domain": { "name": "xyz", "community": { "name": "file" } } } } ]',
'batchSize': '10000',
'file': '@filename.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}
files = {'file': ('import_file', open('filename.xlsx', 'rb'), 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', {'Expires': '0'})}

headers = {
  'accept': 'application/json',
  'Authorization': 'Basic xyz',
  'Content-Type': 'multipart/form-data',
}

response = requests.request("POST", url, headers=headers, data=payload, files=files)

print(response.text)

对于从 python 发送的请求,在 Wireshark 中看不到边界。以下屏幕截图适用于 CURL

试试这个:

import requests

headers = {
    'accept': 'application/json',
    'Authorization': 'Basic xyz',
    'Content-Type': 'multipart/form-data',
}

files = {
    'deleteFile': (None, ''),
    'simulation': (None, ''),
    'fileName': (None, 'import_file'),
    'sheetName': (None, ''),
    'headerRow': (None, ''),
    'sendNotification': (None, ''),
    'sheetIndex': (None, ''),
    'fileId': (None, ''),
    'template': (None, ' [ { "resourceType": "xyz", "type": { "name": "xyz" }, "identifier": { "name": "", "domain": { "name": "xyz", "community": { "name": "test" } } } } ]'),
    'batchSize': (None, '10000'),
    'file': ('filename.xlsx;type', open('filename.xlsx;type', 'rb')),
}

response = requests.post('http://localhost:8080/rest/2.0/import/ejob', headers=headers, files=files)
 

如果不知道您将请求发送到哪个服务器,则很难确定,但我猜您的问题是您正在复制 data 中的 file 表单字段和 files 个参数。

当您在传递给 datafiles 参数的字典中使用相同的键时,Requests 将对请求使用多部分编码,并使用来自两个字典的数据。

例如,当我在我的系统上尝试以下代码时:

requests.post("http://localhost:8000", data={"file": "foo"}, files={"file": "bar"})

它转换为以下 HTTP 请求:

POST / HTTP/1.1
Host: localhost:8000
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 231
Content-Type: multipart/form-data; boundary=2756042e934999f01bd91212ac4ae994

--2756042e934999f01bd91212ac4ae994
Content-Disposition: form-data; name="file"

foo
--2756042e934999f01bd91212ac4ae994
Content-Disposition: form-data; name="file"; filename="file"

bar
--2756042e934999f01bd91212ac4ae994-

在您的情况下,可能是服务器在您的 HTTP 请求中看到第一个 file 表单字段,发现它没有 filename 属性,并决定拒绝该请求。成立。

相反,尝试从 payload 字典中删除 file 键,以便在生成的 HTTP 请求中只有一个 file 表单字段。

import requests

url = "http://localhost:8080/rest/2.0/import/ejob"
payload = {
    "deleteFile": "",
    "simulation": "",
    "fileName": "xyz",
    "sheetName": "",
    "headerRow": "",
    "sendNotification": "",
    "sheetIndex": "",
    "fileId": "",
    "template": ' [ { "resourceType": "Asset", "type": { "name": "xyz" }, "identifier": { "name": "", "domain": { "name": "xyz", "community": { "name": "file" } } } } ]',
    "batchSize": "10000",
}
files = {
    "file": (
        "import_file",
        open("filename.xlsx", "rb"),
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        {"Expires": "0"},
    )
}
headers = {
    "accept": "application/json",
    "Authorization": "Basic xyz",
    "Content-Type": "multipart/form-data",
}

response = requests.post(url, headers=headers, data=payload, files=files)

顺便说一句,部分问题是您在 Python payload 字典的 file 键中使用了 @filename.xlsx,但是 @ 是特殊的 curl 语法,在请求中包含指定文件的内容。

来自curl man page

-F, --form <name=content>

(HTTP SMTP IMAP) For HTTP protocol family, this lets curl emulate a filled-in form in which a user has pressed the submit button. This causes curl to POST data using the Content-Type multipart/form-data according to RFC 2388.

For SMTP and IMAP protocols, this is the mean to compose a multipart mail message to transmit.

This enables uploading of binary files etc. To force the 'content' part to be a file, prefix the file name with an @ sign. To just get the content part from a file, prefix the file name with the symbol <. The difference between @ and < is then that @ makes a file get attached in the post as a file upload, while the < makes a text field and just get the contents for that text field from a file.

在Requests库中,@没有这个效果;相反,它只是作为普通字符插入到请求中。

问题在于在 header 中传递 'Content-Type':'multipart/form-data'。我从有效载荷中删除了文件并修改了 header 以解决问题。