A quick article on how to create a middleware script which accepts the values from a submitted HTML form and sends it to a server on another domain for processing. This applies to Linux Apache MySQL and PHP (LAMP) setups.
Why?
A customer wanted to connect their Mobile App to a third-party API. The third-party only accepts requests from a static and permitted IP address. If the end-user were to make the request, then their own IP address would be the one checked against, and it just wouldn't be manageable to add every new user's IP address to their service. The request has to come from a permitted server with a single IP address.
How?
It's likely that you already know how to submit a HTML form to a server via your Mobile application so the following will only document the process of sending data under the server IP address. We're going to use a PHP script with the cURL function to receive and send the data. cURL is a standard feature on most LAMP setups. If not you can install it from here: http://curl.haxx.se/download.html
I'm demonstrating this with 3 scripts on the intermediary server:
1. to display the HTML form (usually handled by the mobile app)
2. to send the values to the third-party (executable script)
3. to receive the values from the third-party service/server/api
Script #1: The HTML form:
<form id="my_form" method="post" action="./send.php"> Sender: <input id="my_sender" name="my_sender" value="MyMobile" readonly="readonly" /><br /> Receiver: <input id="my_receiver" name="my_receiver" value="JoelLipman.com" readonly="readonly" /><br /> <input type="submit" name="submit" value="Submit" /> </form>
- <form id="my_form" method="post" action="./send.php">
- Sender: <input id="my_sender" name="my_sender" value="MyMobile" readonly="readonly" /><br />
- Receiver: <input id="my_receiver" name="my_receiver" value="JoelLipman.com" readonly="readonly" /><br />
- <input type="submit" name="submit" value="Submit" />
- </form>
Script #2: Send to third-party in XML format: (because that's the format they accept)
Note that in the following example, the XML is sent as a url encoded value paired with a key (posted variables must be key1=value1&key2=value2...)
<?php /* * XML Sender/Client. */ header ("Content-Type:text/xml"); // format submitted values $the_sender = trim($_POST['my_sender']); $the_receiver = trim($_POST['my_receiver']); // set some values $the_thirdparty_url = "http://www.joellipman.com/my_api"; // split out parameters and put in format the third-party is expecting $the_message_xml = "the_message=" . urlencode("<?xml version='1.0' encoding='UTF-8' ?> <the_message> <header sender='".$the_sender."' receiver='".$the_receiver."' mode='TEST'></header> </the_message>"); // We send XML via CURL using POST with a http header of text/xml. $ch = curl_init(); // set URL and other appropriate options curl_setopt($ch, CURLOPT_URL, $the_thirdparty_url); // where to send the variables curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $the_message_xml); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); curl_setopt($ch, CURLOPT_REFERER, 'receive.php'); // my receiving file curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $ch_result = curl_exec($ch); curl_close($ch); // Print CURL result. echo $ch_result; ?>
- <?php
- /*
- * XML Sender/Client.
- */
- header ("Content-Type:text/xml");
- // format submitted values
- $the_sender = trim($_POST['my_sender']);
- $the_receiver = trim($_POST['my_receiver']);
- // set some values
- $the_thirdparty_url = "http://www.joellipman.com/my_api";
- // split out parameters and put in format the third-party is expecting
- $the_message_xml = "the_message=" . urlencode("<?xml version='1.0' encoding='UTF-8' ?>
- <the_message>
- <header sender='".$the_sender."' receiver='".$the_receiver."' mode='TEST'></header>
- </the_message>");
- // We send XML via CURL using POST with a http header of text/xml.
- $ch = curl_init();
- // set URL and other appropriate options
- curl_setopt($ch, CURLOPT_URL, $the_thirdparty_url); // where to send the variables
- curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $the_message_xml);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
- curl_setopt($ch, CURLOPT_REFERER, 'receive.php');  // my receiving file
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- $ch_result = curl_exec($ch);
- curl_close($ch);
- // Print CURL result.
- echo $ch_result;
- ?>
Script #3: process returned data
<?php /* * XML Server. */ // We use php://input to get the raw $_POST results. $xml_post = file_get_contents('php://input'); // If we receive data, save it. if ($xml_post) { $xml_file = 'received_xml_' . date('Y_m_d-H-i-s') . '.xml'; $fh = fopen($xml_file, 'w') or die(); fwrite($fh, $xml_post); fclose($fh); // Return, as we don't want to cause a loop by processing the code below. return; } ?>
- <?php
- /*
- * XML Server.
- */
- // We use php://input to get the raw $_POST results.
- $xml_post = file_get_contents('php://input');
- // If we receive data, save it.
- if ($xml_post) {
- $xml_file = 'received_xml_' . date('Y_m_d-H-i-s') . '.xml';
- $fh = fopen($xml_file, 'w') or die();
- fwrite($fh, $xml_post);
- fclose($fh);
- // Return, as we don't want to cause a loop by processing the code below.
- return;
- }
- ?>
UPDATE 2015 - Implementation
Finally I opted with a single PHP script that receives the variables from a mobile application, talks to the third-party API, parses the response, and returns the data to the mobile app. Additionally, the client wanted the reply to mobile in JSON format so we will extract the values with SIMPLEXML_LOAD_STRING (as of PHP 5), store the values in an array for easy encoding to JSON. This is a cut-down version of my final script:
// set headers for JSON file header('Content-Type: text/javascript; charset=utf8'); header('Access-Control-Allow-Origin: http://mobile.joellipman.com/'); header('Access-Control-Max-Age: 3628800'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); // the posted XML $the_message_xml="<?xml version='1.0' encoding='UTF-8' ?><message><request><my_xml_string /></request></message>"; // We send XML via cURL using POST with a http header of text/xml. $ch = curl_init(); // set URL and other appropriate options curl_setopt($ch, CURLOPT_URL, "http://api.myexample.com"); // action where to POST the variables curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $the_message_xml); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); curl_setopt($ch, CURLOPT_REFERER, 'receive.php'); // my receiving file curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // execute and store $ch_result = curl_exec($ch); curl_close($ch); // Store cURL result $api_result = simplexml_load_string($ch_result) or die("Error: Cannot create object"); // Declare main array to store all variables $reply_array = array(); // parse out some global values we want from XML if($api_result->reply!=null){ // our own script reply to the request $reply_array["sys"] = array("message"=>"ok", "code"=>200); // parse and store the attribute value "entry_count" of element "room_rate_list". // NOTE: also declare the datatype or this won't work! $entry_count = (int) $api_result->reply->room_rate_list['entry_count']; // parse and store the attribute value "currency" of first entry in element "room_rate_list". // NOTE: declare the datatype for string as (string) or this won't work! $api_currency = (string) $api_result->reply->room_rate_list->room_rate[0]['currency']; $reply_array["data"] = array("entry_count"=>$entry_count, "currency"=>$api_currency); } else { // our own script reply to the request $reply_array["sys"] = array("message"=>"ERROR", "code"=>404); } // return and print the JSON $json_array = json_encode($reply_array); echo $json_array; // field validation, variable formatting/transform rules, and user logging are also done via this script.
- // set headers for JSON file
- header('Content-Type: text/javascript; charset=utf8');
- header('Access-Control-Allow-Origin: http://mobile.joellipman.com/');
- header('Access-Control-Max-Age: 3628800');
- header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
- // the posted XML
- $the_message_xml="<?xml version='1.0' encoding='UTF-8' ?><message><request><my_xml_string /></request></message>";
- // We send XML via cURL using POST with a http header of text/xml.
- $ch = curl_init();
- // set URL and other appropriate options
- curl_setopt($ch, CURLOPT_URL, "http://api.myexample.com"); // action where to POST the variables
- curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $the_message_xml);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
- curl_setopt($ch, CURLOPT_REFERER, 'receive.php');  // my receiving file
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- // execute and store
- $ch_result = curl_exec($ch);
- curl_close($ch);
- // Store cURL result
- $api_result = simplexml_load_string($ch_result) or die("Error: Cannot create object");
- // Declare main array to store all variables
- $reply_array = array();
- // parse out some global values we want from XML
- if($api_result->reply!=null){
- // our own script reply to the request
- $reply_array["sys"] = array("message"=>"ok", "code"=>200);
- // parse and store the attribute value "entry_count" of element "room_rate_list".
- // NOTE: also declare the datatype or this won't work!
- $entry_count = (int) $api_result->reply->room_rate_list['entry_count'];
- // parse and store the attribute value "currency" of first entry in element "room_rate_list".
- // NOTE: declare the datatype for string as (string) or this won't work!
- $api_currency = (string) $api_result->reply->room_rate_list->room_rate[0]['currency'];
- $reply_array["data"] = array("entry_count"=>$entry_count, "currency"=>$api_currency);
- } else {
- // our own script reply to the request
- $reply_array["sys"] = array("message"=>"ERROR", "code"=>404);
- }
- // return and print the JSON
- $json_array = json_encode($reply_array);
- echo $json_array;
- // field validation, variable formatting/transform rules, and user logging are also done via this script.
Issues?
- Internal Server Error - I can get this when the third-party API doesn't return expected XML. Ensure that CURLOPT_POSTFIELDS is a string of paired values (ie. key1=value1&key2=value2&key3=value3).
- DataType or value is blank - the data that gets parsed with SIMPLEXML_LOAD_STRING needs to have each variable's datatype declared or it will return as an object. Prefix with (int) for numbers/integers and (string) for strings.