I want to convert my php code to python code.
$u = 'http://httpbin.org/post';
$req = array(
'check' => 'command',
'parameters' => array(
0 => array('parameter' => '1', 'description' => '2'),
1 => array('parameter' => '3', 'description' => '4')
)
);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $u);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($req));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
$response = curl_exec($curl);
php test.php
{
"args": {},
"data": "",
"files": {},
"form": {
"check": "command",
"parameters[0][parameter]": "1",
"parameters[0][description]": "2",
"parameters[1][parameter]": "3",
"parameters[1][description]": "4"
},
this code successfully fetching data from remote api, but when i try to write it with Python requests – array of parameters is sending with wrong data.
url = 'http://httpbin.org/post'
req = {'check': 'command', 'parameters': ({'parameter': '1', 'description': '2'}, {'parameter': '3', 'description': '4'})}
try:
fetch = requests.post(url, data = req)
except requests.exceptions.RequestException as e:
print(e)
print(fetch.text)
python3 test.py
{
"args": {},
"data": "",
"files": {},
"form": {
"check": "command",
"parameters": [
"parameter",
"description",
"parameter",
"description"
]
},
“Parameters”, sended by my Python script is invalid
Advertisement
Answer
PHPs http_build_query
and it’s corresponding $_GET
and $_POST
parsing are completely arbitrary in how they work. Thus you must implement this functionality yourself.
Let’s compare the outputs of PHPs http_build_query
to pythons urlencode
(which requests uses internally to build the parameters)
PHP
$req = array(
'check' => 'command',
'parameters' => array(
0 => array('parameter' => '1', 'description' => '2'),
1 => array('parameter' => '3', 'description' => '4')
)
);
$query = http_build_query($req);
$query_parsed = urldecode($query);
echo $query;
echo $query_parsed;
Result:
check=command¶meters%5B0%5D%5Bparameter%5D=1¶meters%5B0%5D%5Bdescription%5D=2¶meters%5B1%5D%5Bparameter%5D=3¶meters%5B1%5D%5Bdescription%5D=4
check=command¶meters[0][parameter]=1¶meters[0][description]=2¶meters[1][parameter]=3¶meters[1][description]=4
Python
from urllib.parse import urlencode, unquote
req = {'check': 'command', 'parameters': ({'parameter': '1', 'description': '2'}, {'parameter': '3', 'description': '4'})}
query = urlencode(req)
query_parsed = unquote(query)
print(query)
print(query_parsed)
Result:
check=command¶meters=%28%7B%27parameter%27%3A+%271%27%2C+%27description%27%3A+%272%27%7D%2C+%7B%27parameter%27%3A+%273%27%2C+%27description%27%3A+%274%27%7D%29
check=command¶meters=({'parameter':+'1',+'description':+'2'},+{'parameter':+'3',+'description':+'4'})
This looks quite a bit different but apparently conforms to the standard and thus httpbin interprets this correctly.
To make python behave the same as PHP, I’ve adapted this answer to create the following:
from collections.abc import MutableMapping
from urllib.parse import urlencode, unquote
def flatten(dictionary, parent_key=False, separator='.', separator_suffix=''):
"""
Turn a nested dictionary into a flattened dictionary
:param dictionary: The dictionary to flatten
:param parent_key: The string to prepend to dictionary's keys
:param separator: The string used to separate flattened keys
:return: A flattened dictionary
"""
items = []
for key, value in dictionary.items():
new_key = str(parent_key) + separator + key + separator_suffix if parent_key else key
if isinstance(value, MutableMapping):
items.extend(flatten(value, new_key, separator, separator_suffix).items())
elif isinstance(value, list) or isinstance(value, tuple):
for k, v in enumerate(value):
items.extend(flatten({str(k): v}, new_key, separator, separator_suffix).items())
else:
items.append((new_key, value))
return dict(items)
req = {'check': 'command', 'parameters': ({'parameter': '1', 'description': '2'}, {'parameter': '3', 'description': '4'})}
req = flatten(req, False, '[', ']')
query = urlencode(req)
query_parsed = unquote(query)
print(query)
print(query_parsed)
Which outputs
check=command¶meters%5B0%5D%5Bparameter%5D=1¶meters%5B0%5D%5Bdescription%5D=2¶meters%5B1%5D%5Bparameter%5D=3¶meters%5B1%5D%5Bdescription%5D=4
check=command¶meters[0][parameter]=1¶meters[0][description]=2¶meters[1][parameter]=3¶meters[1][description]=4
which seems to be what you want.
Now you should be able to pass the result of flatten
as data
to get the desired behaviour.