Verzeihung, dieser Artikel ist derzeit nicht auf Deutsch verfügbar.

Post PHP To Python

Veröffentlicht:

A couple years ago I got put on a project that needed to have a server running PHP send data to a server running Django on Python.  The request always received a response stating that none of the required fields had been submitted.  The response included the original data after it had been “validated,” except all of the fields were then blank!  It was as if the Python validator was just wiping the data it received.

The guys that originally built the PHP site couldn't figure it out.  If you browsed to the URL of the API request on the Python site, it auto-generated a form which you could fill out and submit.  Another collegue couldn't understand why I couldn't get the API call to work from the PHP site when submitting the form on the Python site worked, until I pointed out that the auto-generated form was just the Python site talking to itself, so of course it worked.

There were a number of issues I was able to resolve on my own, but I couldn't hammer out the final point until I stumbled across a post that was largely unrelated to my issue but happened to mention a detail that proved critical.

The PHP data being posted, collected from a webbrowser form, initially looked something like this:

$data = [
    'Product' => '2691',
    'ExtendedWarranty' => 'No',
    'Application' => [
        'TorqueGrade' => null,
        'Color' => 'green'
    ],
    'ShippingAddress' => [
        'FirstName' => 'George',
        'LastName' => 'Elroy',
        'Email' => 'george.elroy@astro.net',
        'Phone' => '(802)867-5309',
        'Address1' => 'Welker Lane',
        'Address2' => null,
        'City' => 'Bristol',
        'State' => 'VT',
        'Zip' => '5009',
        'Country' => 'US'
    ],
    'BillingAddress' => [
        'Address1' => 'Welker Lane',
        'Address2' => null,
        'City' => 'Bristol',
        'State' => 'VT',
        'Zip' => '5009',
        'Country' => 'US'
    ],
    'Coupon' => 'ORBITTY',
    'Payment' => [
        'Amount' => '2067'
    ]
];

The first problem was that an array with named keys was being sent.  Python technically doesn't support arrays—it has “lists”—and in any case it doesn't support arrays/lists with named indexes.  This might have actually been okay and parsed to an object if it had been a single-dimensional array, but being a multi-dimensional array, it resulted in funky member names like ShippingAddress[FirstName] (with [ and ] as actual parts of the member name) which would not resolve to any values.  This was solved by converting each array to an object in PHP.

Secondly, datatypes contrary to what was expected were being sent, e.g. Yes/No was being sent instead instead of boolean true/false, and an integer field that was allowed to be left blank in the webrowser form should have been parsed to zero.

The corrected data format ended up looking like this:

$data = (object)[ // Each array converted to an object
    'Product' => '2691',
    'ExtendedWarranty' => false, // Changed "Yes"/"No" in the web form to "1"/"0" then parsed to boolean on the PHP backend before forwarding to the Python server
    'Application' => (object)[
        'TorqueGrade' => 0, // Parsed blank integer fields to zero
        'Color' => 'green'
    ],
    'ShippingAddress' => (object)[
        'FirstName' => 'George',
        'LastName' => 'Elroy',
        'Email' => 'george.elroy@astro.net',
        'Phone' => '(802)867-5309',
        'Address1' => 'Welker Lane',
        'Address2' => null,
        'City' => 'Bristol',
        'State' => 'VT',
        'Zip' => '5009',
        'Country' => 'US'
    ],
    'BillingAddress' => (object)[
        'Address1' => 'Welker Lane',
        'Address2' => null,
        'City' => 'Bristol',
        'State' => 'VT',
        'Zip' => '5009',
        'Country' => 'US'
    ],
    'Coupon' => 'ORBITTY',
    'Payment' => (object)[
        'Amount' => '2067'
    ]
];

The clincher however proved to be a critical lack of header information.  The post data needed to be converted to JSON, and then a header specifying content-type as JSON was needed:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, 1);
// These next two lines were the key to making the submission work
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json' ]);
$result = curl_exec($ch);
print 'curl info: ' . print_r(curl_getinfo($ch), true) . "\n";
print 'curl error: ' . print_r(curl_error($ch), true) . "\n";
curl_close($ch);
var_dump($result);