Recipe 16.3. Writing an XML-RPC Client
Credit: John-Mason Shackelford
Problem
You want to call a remote method through the XML-RPC web service protocol.
Solution
Use Michael Neumann's xmlrpc4r library, found in Ruby's standard library.
Here's the canonical simple XML-RPC example. Given a number, it looks up the name of a U.S. state in an alphabetic list:
require 'xmlrpc/client' server = XMLRPC::Client.new2('http://betty.userland.com/RPC2') server.call('examples.getStateName', 5) # => "California"
Discussion
XML-RPC is a language-independent solution for distributed systems that makes a simple alternative to SOAP (in fact, XML-RPC is an ancestor of SOAP). Although it's losing ground to SOAP and REST-style web services, XML-RPC is still used by many blogging engines and popular web services, due to its simplicity and relatively long history.
A XML-RPC request is sent to the server as a specially-formatted HTTP POST request, and the XML-RPC response is encoded in the HTTP response to that request. Since most firewalls allow HTTP traffic, this has the advantage (and disadvantage) that XML-RPC requests work through most firewalls. Since XML-RPC requests are POST requests, typical HTTP caching solutions (which only cache GETs) can't be used to speed up XML-RPC requests or save bandwidth.
An XML-RPC request consists of a standard set of HTTP headers, a simple XML document that encodes the name of a remote method to call, and the parameters to pass to that method. The xmlrpc4r library automatically converts between most XML-RPC data types and the corresponding Ruby data types, so you can treat XML-RPC calls almost like local method calls. The main exceptions are date and time objects. You can pass a Ruby Date or Time object into an XML-RPC method that expects a dateTime.iso8601 parameter, but a method that returns a date will always be represented as an instance of XMLRPC::DateTime.
Table 16-1 lists the supported data types of the request parameters and the response.
Table 16-1. Supported data typesXML-RPC data type | Description | Ruby equivalent |
---|
int | Four-byte signed integer | Fixnum or Bignum | boolean | 0 (false) or 1 (true) | TrueClass or FalseClass | string | Text or encoded binary data; only the characters < and & are disallowed and rendered as HTML entities | String | double | Double-precision signed floating point number | Float | dateTime.iso8601 | Date/time in the format YYYYMMDDTHH:MM:SS (where T is a literal) | XMLRPC::DateTime | base64 | base64-encoded binary data | String | struct | An unordered set of key value pairs where the name is always a String and the value can be any XML-RPC data type, including netsted a nested struct or array | Hash | array | A series of values that may be of any of XML-RPC data type, including a netsted struct or array; multiple data types can be used in the context of a single array | Array |
Note that nil is not a supported XML-RPC value, although some XML-RPC implementations (including xmlrpc4r) follow an extension that allows it.
An XML-RPC response is another XML document, which encodes the return value of the remote method (if you're lucky) or a "fault" (if you're not). xmlrpc4r parses this document and transforms it into the corresponding Ruby objects.
If the remote method returned a fault, xmlrpc4r raises an XMLRPC::FaultException. A fault contains an integer value (the fault code) and a string containing an error message. Here's an example:
begin server.call('noSuchMethod') rescue XMLRPC::FaultException => e puts "Error: fault code #{e.faultCode}" puts e.faultString end # Error: fault code 7 # Can't evaluate the expression because the name "noSuchMethod" hasn't been defined.
Here's a more interesting XML-RPC example that searches an online UPC database:
def lookup_upc(upc) server = XMLRPC:: Client.new2('http://www.upcdatabase.com/rpc') begin response = server.call('lookupUPC', upc) return response['found'] ? response : nil rescue XMLRPC::FaultException => e puts "Error: " puts e.faultCode puts e.faultString end end
product = lookup_upc('018787765654') product['description'] # => "Dr Bronner's Peppermint Oil Soap" product['size'] # => "128 fl oz"
lookup_upc('no such UPC') # => nil
See Also
Michael Neumann's xmlrpc4rHOWTO (http://www.ntecs.de/projects/xmlrpc4r/howto.html) The XML-RPC Specification (http://www.xmlrpc.com/spec) The extension to XML-RPC that lets it represent nil values (http://ontosys.com/xml-rpc/extensions.php) The Ruby Developer's Guide, published by Syngress and edited by Michael Neumann, contains over 20 pages devoted to implementing XML-RPC clients and servers with xmlrpc4r. Recipe 15.8, "Creating a Login System," shows how to serve XML-RPC requests from within a Rails application
|
No comments:
Post a Comment