1

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

1 Answer 1

3

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&parameters%5B0%5D%5Bparameter%5D=1&parameters%5B0%5D%5Bdescription%5D=2&parameters%5B1%5D%5Bparameter%5D=3&parameters%5B1%5D%5Bdescription%5D=4
check=command&parameters[0][parameter]=1&parameters[0][description]=2&parameters[1][parameter]=3&parameters[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&parameters=%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&parameters=({'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&parameters%5B0%5D%5Bparameter%5D=1&parameters%5B0%5D%5Bdescription%5D=2&parameters%5B1%5D%5Bparameter%5D=3&parameters%5B1%5D%5Bdescription%5D=4
check=command&parameters[0][parameter]=1&parameters[0][description]=2&parameters[1][parameter]=3&parameters[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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.