Introducing REST


REST was described as an architectural style by Roy Thomas Fielding as part of his doctoral dissertation in the year 2000 (Architectural Styles and the Design of Network-based Software Architectures, Chapter 5). For the curious, it is available online at www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm.

Note 

For the focus of this chapter, REST is discussed as per its application to web services.

REST is a stateless method for applications to present requests to the required service. Each request has two essential parts: the endpoint (usually a URL) and the message indicating the requested action. That might sound scary, but realistically it isn't so different from a web browser requesting a web page — it knows the endpoint (the URL for the web page in question) and it knows what action it would like to perform (download the page in question). Also just like web browsing, there are methods to mimic a stateful connection — with web browsing these are called sessions; REST has similar constructs.

While architecturally speaking REST can take place over any transport medium (not just HTTP/HTTPS), in the context of APIs, HTTP is the logical choice. We (the programmers) already have great experience dealing with communications over HTTP, and have suites of tools at our disposal to aid development and problem resolution.

How REST Works

Generally speaking, a REST request will involve sending a request to a special URL (similar to what you would see after filling out a form using the GET method), then receiving an XML document containing the server's response. The XML response is then parsed, and the desired information is extracted and acted upon.

Each REST request generally has several common elements:

  • Endpoint URL — The full address for the desired script. A REST service might have only a single script that handles all request types, or different scripts for different request types.

  • Developer ID — Most REST services require some sort of developer ID or key to be sent with each request. This identifies the origin of the request and is generally used for tracking purposes. Some services may use this value to limit the number of queries run during a given time frame.

  • Desired action — Few servers have a unique endpoint for all possible requests. As such, it is generally required to include the desired action in the request.

  • Parameters — Several parameters will need to be included with the request to provide the requested action with some context (for example, the desired action might be a search; the parameters might be a type, and the keywords values of book and style).

With those elements in mind, you can create a theoretical request:

 http://library.example.com/api.php?devkey=123&action=search&type=book&keyword=style 

Here a request is sent to the endpoint http://library.example.com/api.php, with a developer key of 123. The desired action is search, and type and keyword parameters are included with values of book and style. Given that request, the response would look something like this:

 <?xml version="1.0" encoding=" UTF-8"?> <LibraryAPI xmlns="http://library.example.com/api/spec"> <Request>  <RequestId>123a456</RequestId>  <Parameters>   <Argument Name="devkey" Value="123" />   <Argument Name="action" Value=" search" />   <Argument Name="type" Value=" book" />   <Argument Name="keyword" Value=" style" />  </Parameters> </Request> <Response>  <ResultCount>2</ResultCount>  <Item>   <Title>Style Book Vol 1</Title>   <Status>Out</Status>   <Holds>3</Holds>   <CopiesOnHand>2</CopiesOnHand>   <Author>Jon Doe</Author>  </Item>  <Item>   <Title>Style Book Vol 2</Title>   <Status>In</Status>   <Holds>0</Holds>   <CopiesOnHand>1</CopiesOnHand>   <Author>Jon Doe</Author>  </Item> </Response> </LibraryAPI> 

You can see that the response has several structural elements. First, it declares itself to be XML 1.0 and uses UTF-8 for encoding. The LibraryAPI element is the root element of this document and includes the specified namespaces. Second, the Request section; it is common for REST requests to include all information sent with the request in the response. This adds clarity, and can ease programming on the requestor's end. Here you see each of the four elements passed to the service.

Finally, looking a bit closer, you will notice that in this case the response has returned some metadata about the results, shown in the ResultCount tag, along with the result items themselves. For this request you see each book is encapsulated within an Item element, which has five children that describe specific attributes of the books.

Implementing REST

Now that you know basically how it works, it's time to look at how to use REST for your own purposes. There are two sides to this tale, the first is how to generate legitimate REST requests, and the second is how to handle the responses correctly.

Generating Requests

When it comes to generating the request, you have three main options. First, you can generate the request manually, using PHP's header functions. This gives you complete flexibility in generating the request, but does involve the most coding. Second, you can use one of PHP's built-in request functions such as file_get_contents() or file()/fopen(), fread(), and fclose(). With this method, a lot of the detailed information is handled automatically by PHP, and you receive the same response. Finally,you can use a custom class designed to be used with the API in question. Generally these classes require nothing more than the parameter list, and will return the results in the form of a custom object or make them accessible through a class.

Manual Generation

Generating requests manually is only tricky the first time, after that, code-reuse and modularity kick in. Conceptually the function that will generate the request is pretty basic. First, all the request parameters are prepared to ensure proper transmission. Next, the URL for the endpoint is generated, then parsed and broken up into its component parts. Finally, the request itself is sent using sockets:

 function callAPI($endpoint, $devkey, $action, $type, $keyword) {   $action = urlencode($action);   $type = urlencode($type);   $keyword = urlencode($keyword); 

Three of the passed parameters are URL encoded. This is necessary to ensure they are passed properly over the URL. In the previous example, a search for "style" was performed; if the search had instead been "style book," the space would have required encoding, resulting in style%20book. I have neglected encoding the devkey variables, trusting that the issuing authority took how it would be used into consideration when creating them.

   $url = $endpoint . "?devkey=$devkey&action=$action&type=$type&keyword=$keyword";   $url_info = parse_url($url);   $host = $url_info['host'];   $path = $url_info['path'] . "?" . $url_info[‘query'];   $data = ""; 

Here the URL itself is generated, including the now URL-encoded parameters. The URL needs to be deconstructed down to its component parts for use in the raw socket connection.

   $fp=fsockopen($host, 80);   fputs($fp, "POST ". $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/x-www-form-urlencoded\r\n");   fputs($fp, "Content-Length: ". strlen($data) . "\r\n\r\n");   fputs($fp, "$data"); 

Here the information generated by previous code is finally sent. The first fputs() line sends the path to the requested document, and the second line specifies the desired host.

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

Finally, the response is retrieved and the content of the response is returned. You learn how to handle responses in the sections dealing with that side of things shortly.

Quick Generation

Utilizing PHP's built-in file functions, the same process can be completed with much less code (though you do have less flexibility). Conceptually this function works the same as the previous one, except all the file socket calls are replaced with one call to file_get_contents():

 function callAPIQuick($endpoint, $devkey, $action, $type, $keyword) {   $action = urlencode($action);   $type = urlencode($type);   $keyword = urlencode($keyword);   $url = $endpoint . "?devkey=$devkey&action=$action&type=$type&keyword=$keyword";   $response = @file_get_contents($url);   return $response; } 

There really isn't much to explain with this example. The URL encoding was discussed previously, and the single file_get_contents() call handles all the magic. The ampersand in front of the function call is used to suppress any warnings that may arise from a non-existent file or URL, because these should be handled by the calling function (file_get_contents() will return false in these instances). In pre-PHP5 environments you will need to use fopen() instead of file_get_contents(). Some flexibility is lost with this request type, because you can no longer set custom headers or optional headers, which may be required or very desirable depending on the API with which you are interacting.

Automated Tools

As the popularity of web services increases, so will the prevalence of prebuilt classes to handle the dirty work of actually interacting with the server. If the service you want to interact with has a class available, it is definitely worth looking into. Accessing the class will of course be dependent on the class itself. It should come with sufficient documentation, and access will likely not differ too much from the earlier examples — just with a little more error checking (you hope). Something to keep in mind is that many prebuilt modules are developed and maintained by third parties, and as such you might have to wait a while after new features are released on the API for them to become available with your class.

Handling the Response

How you deal with the response depends on which method of sending the request you choose. If you generated the request either manually or with the aid of one of PHP's built-in functions (like file_get_contents()) you will also need to manually handle the response. If you used a third-party module, it will have its own interface for retrieving results.

Manually Parsing the Response

The response provided by the server should be an XML document; luckily, XML was designed to be easy to parse. Unfortunately, no matter how it was designed, manually parsing anything usually isn't a lot of fun. PHP5 comes with SimpleXML, which makes handling XML documents a breeze. PHP4 users don't have SimpleXML; however, a few third-party modules like MiniXML are available that perform similar functions. MiniXML was explored in Chapter 3, so take a look back if you skipped it — the interface is a little different but structurally you should be able to use it the same way.

Once you have received the response, sticking it into a SimpleXML object should be a breeze. Using the previous library example and request function, you end up with something like this:

 $response = callAPIQuick('http://library.example.com/api.php', '123', 'search', 'book', ‘style'); if ($response) {   $xml = simplexml_load_string($response);   print_r($xml); }else {   echo "Error loading feed"; } 

Here, the response is not false (and hence something, presumably the XML you were hoping for, was returned). Note that this assumption is generally pretty valid. When a server providing an API encounters an error, it should provide the error in a nice XML format. The simplexml_load_string() function takes the response and turns it into an XML object that can be directly accessed, iterated through, and so on. Finally, the print_r() function results in a user-friendly output showing the contents of the object, shown here:

 SimpleXMLElement Object (   [Request] => SimpleXMLElement Object   (     [RequestId] => 123a456     [Parameters] => SimpleXMLElement Object     (       [Argument] => Array       (         [0] => SimpleXMLElement Object         (         )         [1] => SimpleXMLElement Object         (         )         [2] => SimpleXMLElement Object         (         )         [3] => SimpleXMLElement Object         (         )       )     )   )   [Response] => SimpleXMLElement Object   (     [ResultCount] => 2     [Item] => Array     (       [0] => SimpleXMLElement Object       (         [Title] => Style Book Vol 1         [Status] => Out         [Holds] => 3         [CopiesOnHand] => 2         [Author] => Jon Doe       )       [1] => SimpleXMLElement Object       (         [Title] => Style Book Vol 2         [Status] => In         [Holds] => 0         [CopiesOnHand] => 1         [Author] => Jon Doe       )     )   ) ) 

Looking at that output, a couple things should be immediately obvious:

  • SimpleXML does a lot of really useful things for you very quickly.

  • The resultant objects don't display attributes (see the argument list under parameters). The data is in there, it just isn't shown with a print_r().

  • Arrays start counting at 0 (just like everywhere else in PHP, it's just something to keep in mind).

  • The Item array is just begging to be handled with a foreach() loop.

Bearing all that information in mind, a couple of quick lines of code are all that is required to explore the content more fully.

   echo "You searched for: {$xml->Request->Parameters->Argument[3]->Value}<br>";   echo "Here are your {$xml->Response->ResultCount} results<br>";   foreach($xml->Response->Item AS &$item)   {     echo "{$item->Title} by {$item->Author}<br>";   } 

Here the search query and result count is presented, and then the results themselves are iterated through. The syntax gets a little weird when dealing with arrays (as demonstrated when the search query is printed), so it is often sensible to iterate through them for clarity.

REST is an effective method of querying remote APIs when it is permissible for the request portion of your transaction to take place in the clear. Creating REST queries is as easy as URL-encoding the required parameters and specifying an endpoint for the call. Dealing with REST responses can be a little trickier; however, by leveraging tools like SimpleXML, it too can be completed quickly.




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