More AJAX and SOAP web service interaction

Well I lost a day of my life getting this all to work so I thought I would save others their precious time. I recently embarked on interacting with a web service I built for a new service I’m launching. I decided to make the web client user-friendly and leverage AJAX to interact with the web service. There are hundreds of articles out there on the topics and some tools from IBM and others, but most were overkill or forced the result data to be converted into <div> or <table> elements.

When I had mastered the XMLHttpRequest object I ran into an issue, I wanted to test out my app by loading an xml document containing a SOAP request envelope and send it to my web service as a POST. I also wanted to reuse my xmlHttp object. It sounds simple but I ran into the following issues:

  1. Mozilla errors when trying xmlHttp.open a second time
  2. Security errors when trying to access an outside URL
  3. cURL issues sending text/xml Content-Type as POST
  4. XML declaration issues where PHP added underscore (xml_version)
  5. Likely others, but I got them to work

Here’s how I solved them:

1. Adding a check for readyState == 4 and adding xmlHttp.abort()
2. Adding a proxy script for passing on HTTP POST and returning response
3. Overriding cURL defaults using setopt
4. Using str_replace(’xml_version’, ‘xml version’, $postvars)
5. Lots of searching and trial/error :)

Below I have posted the contents of my PHP proxy script. My web service was built using PEAR SOAP library which required content to be sent as text/xml. I ran into an issue that took hours to finally “see” and for some reason PHP was adding an underscore between the first XML element name and attribute? I used an ugly workaround until I figure out why it did it (php 4.3.9 on macbook pro - intel).

(NOTICE: my blog adds \ to escape double quotes " so you have to remove those if you plan on using any code posted).

PHP proxy script: ws_proxy.php by Mike Sparr, Goomzee Corporation

< ?php
/*
  PHP PROXY SCRIPT (requires cURL)
*/
// Allowed hostname (hard-code this for security)
define ('HOSTNAME', 'http://webservices.goomzee.com');

// Get the REST call path from the AJAX application
// Is it a POST or a GET?
$path = ($_POST['ws_path']) ? $_POST['ws_path'] : $_GET['ws_path'];
$url = HOSTNAME.$path;

// Open the Curl session
$session = curl_init($url);

// If it's a POST, put the POST data in the body
if ($_POST) {
	$postvars = '';
	while ($element = current($_POST)) {
		$postvars .= key($_POST).'='.$element.'&';
		next($_POST);
	}
	$postvars = substr($postvars, 0, -1);  // removing trailing &
	$postvars = str_replace('xml_version', 'xml version', $postvars);  // fix xml declaration bug?
	$postvars = stripslashes($postvars);
	curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars);
}

// Set outgoing request headers for SOAP requests
$headers = apache_request_headers();
$header_array = Array( "Content-Type: text/xml", "SOAPAction: " . $headers['SOAPAction']);
curl_setopt ($session, CURLOPT_HTTPHEADER, $header_array);
curl_setopt ($session, CURLOPT_CUSTOMREQUEST, "POST");

// Don't return HTTP headers. Do return the contents of the call
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

// Make the call
$xml = curl_exec($session);

// The web service returns XML. Set the Content-Type appropriately
header("Content-Type: text/xml");

echo stripslashes($xml);
curl_close($session);

?>

Furthermore, if you read my prior post about AJAX scripting, I modified my XMLHttpRequest object instantiation function and called it for each request. I opted for the safer asynchronous usage for my test and implementation to avoid server hanging.

AJAX Web Service invocation (through ws_proxy.php) Test file - Mike Sparr

<html>
<head>
  <title>AJAX test</title>
<script language="javascript">
var xmlHttp;

function newHttpRequest() {
 var httpRequest = null;
 try {
  httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
  }
 catch(ee) {
  try {
   httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
   }
  catch(eee) {
   httpRequest = null;
   }
  }
 if (!httpRequest && typeof XMLHttpRequest != "undefined") {
  httpRequest = new XMLHttpRequest();
  }
 return httpRequest;
 }

function getInfo() {
 if (xmlHttp && xmlHttp.readyState != 0) {
  xmlHttp.abort();
  }

 xmlHttp = newHttpRequest();
 if (xmlHttp) {
  xmlHttp.open('GET', 'get_info_request.xml', true);
  xmlHttp.onreadystatechange = function() {
    if (xmlHttp.readyState == 4 && xmlHttp.responseText) {
     // do something w/response
      xml = xmlHttp.responseText;

      // ugly formatting I know - just testing :)
      xmlHttp = newHttpRequest();
      if (xmlHttp) {
        xmlHttp.open('POST', 'ws_proxy.php?ws_path=/path/to/endpoint', true);
        xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xmlHttp.setRequestHeader('SOAPAction', '');  // depends on service
        xmlHttp.onreadystatechange = function() {
            if (xmlHttp.readyState ==4 && xmlHttp.responseText) {
	        alert(xmlHttp.responseText);
	   } else {
	       return;
	   }
        }

        // send the SOAP request
         xmlHttp.send(xml);
      }
     } else {
      return;
     }
    }
  }
 // send the request for XML document
 xmlHttp.send(null);
 }
</script>
</head>
<body>
<a href="javascript:getInfo();">Get Info</a>
</body>
</html>

Obviously you will need to have an active web service and change the URL in your proxy script to whatever remote server you want to use (hard-code is recommended; remember your AJAX is javascript that can be read when viewing source - you don’t want people proxying malicious requests through your server). In addition, although for testing I loaded a static XML document containing a SOAP Envelope, you may want to dynamically construct your request (unless you’re just displaying local weather or company stock quote, etc.).

Enjoy!

3 Votes | Average: 4.33 out of 53 Votes | Average: 4.33 out of 53 Votes | Average: 4.33 out of 53 Votes | Average: 4.33 out of 53 Votes | Average: 4.33 out of 5 (3 votes, average: 4.33 out of 5)
Loading ... Loading ...


One Response to “More AJAX and SOAP web service interaction”

  1. Mike Sparr Says:

    Again, like my other source code post, you will need to removed the \ escape characters for the double quotes if you want to use the code. The proxy script requires cURL to be installed on your server, along with PHP. You can perform a search online for other proxy examples - there was one that used an http_class.php and proxy.php files that seemed to work also - I like cURL so I stuck with it. :)

Leave a Reply

You must be logged in to post a comment.