Introducing SOAP


SOAP started off as a Microsoft initiative (pause for the collective gasp from the anti-MS community) in 1997 with the goal to implement remote procedure calls over HTTP. DevelopMentor and Userland joined the discussions and the term SOAP was coined in 1998. Internal politics delayed release of the specification, until Userland broke ranks and published the specification as XML-RPC in the summer of 1998. A flurry of releases and revisions took place over the next few years (incorporating revisions and extensions to the XML specification itself) to arrive at SOAP 1.2 in 2001.

SOAP (like REST) allows a program to request data from a remote server using HTTP. Unlike REST, the request itself is not sent in the request header, but instead within the body of the message. The message itself is an XML document with the root element being an Envelope, which in turn contains a Body element. The Envelope declaration defines the namespaces to be used, and the Body contains the request itself. This additional encapsulation adds transmission overhead, but does provide the benefits of namespacing and variable scope.

The services provided by a SOAP server, as well as the request parameters to make any available calls, are specified within a WSDL file provided by the server. Many modules designed to assist with SOAP development will require this file (either locally, or a link to it), and in fact several generic SOAP clients are out there that can make calls and return results given that file.

How SOAP Works

A SOAP request will involve creating and populating a request envelope, which contains all the required information (as specified by the WSDL document), transmitting that envelope to the API server, and handling the response.

A SOAP request generally contains all of the following elements:

  • SOAP Envelope — With namespace inclusions.

  • SOAP Body — Possibly defining additional namespaces.

  • Desired Action — How the desired action is represented will depend on the API in question. It may be as simple as a parameter, or involve additional namespaces.

  • Developer Key — A unique identifier assigned by the server to the requestor.

  • Request Parameters — Detailing the request being performed.

With that information in mind, a SOAP request, equivalent to the REST request performed in the previous section, can be generated.

 <?xml version="1.0" encoding=" UTF-8" standalone=" no"?> <SOAP-ENV:Envelope   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"   xmlns:xsd="http://www.w3.org/2001/XMLSchema"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   <SOAP-ENV:Body>     <devkey xsi:type=" xsd:int">123</devkey>     <action xsi:type=" xsd:string">search</action>     <type xsi:type=" xsd:string">book</type>     <keyword xsi:type=" xsd:string">style</keyword>   </SOAP-ENV:Body> </SOAP-ENV:Envelope> 

The parameters included in the request are easy to pick out, and though the variable typing isn't of great importance for PHP development, it does come in handy for more strongly typed languages. The missing item here is the endpoint, made clear in the REST example because it was the URL to which the request was posted. SOAP requests, of course, are run against specified URIs, which do not need to be re-specified within the request itself.

Given that request and the responses presented in the previous section, the SOAP response would look like this:

 <?xml version='1.0' encoding='UTF-8'?> <SOAP-ENV:Envelope   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"   xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"   xmlns:xsd="http://www.w3.org/1999/XMLSchema">  <SOAP-ENV:Body>  <LibrarySearchResponse xmlns="http://library.example.com/api/ns">   <RequestInfo>     <devkey xsi:type=" xsd:string">123</devkey>     <action xsi:type=" xsd:string">search</action>     <type xsi:type=" xsd:string">book</type>     <keyword xsi:type=" xsd:string">style</keyword>   </RequestInfo>   <ResponseInfo>    <ResultCount>2</ResultCount>    <Item>     <Title xsi:type=" xsd:string">Style Book Vol 1</Title>     <Status xsi:type=" xsd:string">Out</Status>     <Holds xsi:type=" xsd:int">3</Holds>     <CopiesOnHand xsi:type=" xsd:int">2</CopiesOnHand>     <Author xsi:type=" xsd:string">Jon Doe</Author>    </Item>    <Item>     <Title xsi:type=" xsd:string">Style Book Vol 2</Title>     <Status xsi:type=" xsd:string">In</Status>     <Holds xsi:type=" xsd:int">0</Holds>     <CopiesOnHand xsi:type=" xsd:int">1</CopiesOnHand>     <Author xsi:type=" xsd:string">Jon Doe</Author>    </Item>   </ResponseInfo>  </LibrarySearchResponse>  </SOAP-ENV:Body> </SOAP-ENV:Envelope> 

The SOAP response isn't too different from the REST response shown earlier. In fact, much of the name spacing could actually be omitted (though it is rare to see a SOAP response without it), at which point, with the exception of the additional encapsulation, the two documents would be very similar.

As you should be able to discern from the response shown, it declares itself to be XML 1.0 and uses UTF-8 for encoding. The SOAP-ENV:Envelope element is the root element for the document, and has threenamespaces, including the SOAP-ENV namespace. The Body then contains the LibrarySearchResponse element among other things, which also defines its own namespace.

The RequestInfo parent follows after, and this contains the request parameters that generated the response that follows on from there. Returning request parameters with the response is a common occurrence in SOAP.

Finally, the response itself is returned. Notice that the ResultCount element sits as a direct child of ResponseInfo, and the result items themselves are again stored under a repeating element, Item.

Implementing SOAP

Like REST, implementing SOAP involves both generating requests and then handling the response. Whereas handling the SOAP response is similar to the REST result, generating the SOAP request is quite different.

Generating Requests

Unlike REST, it is rather uncommon to see requests generated manually, though it can still be done. Generally, SOAP requests are either generated with a generic tool (like NuSOAP or Pear:SOAP) or with an application-specific class or module. Manual generation is covered here (a good understanding of how it works will come in useful), as well as NuSOAP.

Application-specific tools are going to be ignored, for a couple reasons. By definition they are application specific, and therefore not very useful in this generic chapter. Secondly, the tools have varying interfaces and functionality levels, and in order to do the topic justice it would require an in-depth look. Finally, I am firmly of the opinion that if you can figure out how to make it work with NuSOAP (or raw, for that matter), you can get the application-specific tool to work too.

Manual Generation

Generating SOAP requests manually isn't too different from generating REST requests. The process of generating the request and actually transmitting it is split into two separate functions, for demonstrating purposes.

For generating the request itself, I have chosen to use a pregenerated string, and merely populate the required values at runtime. There are more complex options (such as creating the document within SimpleXML, or creating it from scratch each run), but they aren't really required:

 function createRequest($devkey, $action, $type, $keyword) {   $request = "<?xml version=\"1.0\" encoding=\" UTF-8\" standalone=\" no\"?> <SOAP-ENV:Envelope   xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"   xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">   <SOAP-ENV:Body>     <devkey xsi:type=\" xsd:int\">$devkey</devkey>     <action xsi:type=\" xsd:string\">$action</action>     <type xsi:type=\" xsd:string\">$type</type>     <keyword xsi:type=\" xsd:string\">$keyword</keyword>   </SOAP-ENV:Body> </SOAP-ENV:Envelope>";   return $request; } 

As you can tell, the function is just about as simple as it can get. Note that the variables haven't been URL-encoded; that's because they aren't being sent in the URL (sounds obvious, but it's also easy to miss).

Actually, calling the API to transmit the request involves borrowing some code from the first REST example, because the request will be sent raw this time.

 function callSOAPAPI($data) {   $url = "http://library.example.com/api/soap/search";   $url_info = parse_url($url);   $host = $url_info[‘host'];   $path = $url_info[‘path']; 

I could have just populated the $host and $path variables at the start, but this should be clearer. In a production system, you could save a few CPU cycles by hardcoding these elements.

   $fp=fsockopen($host, 80);   fputs($fp, "GET ". $path . "HTTP/1.1\r\n");   fputs($fp, "Host: ". $host ."\r\n");   fputs($fp, "Accept: */*\r\n");   fputs($fp, "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n");   fputs($fp, "Connection: close\r\n");   fputs($fp, "Content-Type: application/soap+xml\r\n");   fputs($fp, "Content-Length: ". strlen($data) . "\r\n\r\n");   fputs($fp, "$data"); 

This block is nearly identical to the previous example. The previous example was a POST request, whereas this is a GET request, a change that will be dictated by whatever API you are working with. The Content-Type header is different to accurately reflect what you're sending and this time of course you have $data to send, so Content-Length won't be 0.

   $response="";   while(!feof($fp))   {     $response.=fgets($fp, 128);   }   fclose($fp);   list($http_headers, $http_content)=explode("\r\n\r\n", $response);   return $http_content; } 

This section is also identical to the REST example. Wrapping both those functions to retrieve a response is only barely worth mentioning:

 $request = createRequest('123', 'search', 'book','style); $response = callSOAPAPI($request); 

Wasn't too hard!

Unlike REST, you can't just use file_get_contents() to hit the API, because you need to send the XML body with the request. file_put_contents() wont work either, because you need the response (file_put_contents() returns an int), but there are other options. Pear (http://pear.php.net), for example, has a bunch of HTTP-specific functions that can take some of the headache of manually creating the request off your hands, but still allow you all the flexibility you get with manual creation.

Generation with NuSOAP

For anything other than a one-shot program, I would definitely recommend going with some sort of a SOAP module to make your coding life easier. Although, if you run into problems, tracking them down can be a bit of a pain. I like using a local development box during development, so should things start going awry I can use a packet sniffer to look at the request/response in its raw form.

Conceptually, using NuSOAP isn't too different from completing the task manually. The object is initialized, the payload is created, and the request is sent. The key difference here is that NuSOAP is doing all the dirty work.

 require('../lib/nusoap.php'); $client = new soapclient("http://library.example.com/api/soap/wsdl/", true); 

Here the $client object is created. Two options are available when creating a new soapclient: You can either specify the wsdl file for the service (and set the second parameter to true), or specify the endpoint for the call (and set the second parameter to false). Whenever possible, I like to use the wsdl file; the NuSOAP module can catch some of your mistakes that way, and it ensures that different request types all go to the correct endpoint.

 $params = array(   'devkey'   => '123',   'action'   => 'search',   'type'     => 'book',   'keyword'  => 'style' ); 

Preparing the parameters for transmission is a bit easier than earlier methods.

 $namespace = 'http://library.example.com'; $action = 'http://library.example.com/api/soap/search'; $method = "SearchRequest"; $result = $client->call($method,   array(‘SearchRequest' => $params),   $namespace, $action); 

Finally, the last few parameters are set, and the call itself is made. The resulting object is discussed in the next section.

Handling the Response

Handling a response from a SOAP request is again not too different from the REST response—both are provided in similar XML formats. The SOAP response carries the additional Envelope and Body elements, but often present data in a similar manner within those elements. There is of course some variation between handling the response from a manual request and from a NuSOAP request. Both methods are presented here.

Manually Parsing the Response

Mimicking the output generated with the REST request uses similar code, with a few modifications for the encapsulation scheme used with SOAP.

 echo "You searched for: {$xml->Body->LibrarySearchResponse->RequestInfo->      keyword}<br>"; echo "Here are your {$xml->Body->LibrarySearchResponse->ResponseInfo-    >ResultCount}      results<br>"; foreach($xml->Body->LibrarySearchResponse->ResponseInfo->Item AS &$item) {    echo "{$item->Title} by {$item->Author}<br>"; } 

This will generate identical output as the REST request shown earlier. Note the different syntax used to acquire the search keyword. It isn't an attribute this time, so access is different.

Parsing the Response with NuSOAP

Accessing the object provided by NuSOAP is a little different from the methods used with SimpleXML, but the internal data structure is quite similar. Modifying the code to work with the NuSOAP object only takes a few moments.

 echo "You searched for: ".   $xml['Body']['LibrarySearchResponse']['RequestInfo']['keyword'] . "<br>"; echo "Here are your   {$xml['Body']['LibrarySearchResponse']['ResponseInfo']['ResultCount']}   results<br>"; foreach($xml['Body']['LibrarySearchResponse']['ResponseInfo']['Item']   AS &$item) {   echo "{$item[‘Title']} by {$item[‘Author']}<br>"; } 

With NuSOAP, the internal data is accessed much the same way as an associative array, so rather than the OO method of using -> to access child elements, further array information is included. This element will output identically to the previous example.

SOAP is an effective method of querying APIs when the additional overhead is permissible. The encapsulation of all elements allows for easy reading, and variable scope within the request. Creating SOAP requests can be as easy as writing them out once, then just replacing key variables. Alternatively, it can also be accomplished by using a tool such as NuSOAP. Accessing the SOAP response can be accomplished in much the same manner as the REST response when the request has been completed manually. In the case of requests completed with NuSOAP, the access method is structurally identical, with only a few minor changes to the syntax used.




Professional Web APIs with PHP. eBay, Google, PayPal, Amazon, FedEx, Plus Web Feeds
Professional Web APIs with PHP. eBay, Google, PayPal, Amazon, FedEx, Plus Web Feeds
ISBN: 764589547
EAN: N/A
Year: 2006
Pages: 130

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net