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 Neumanns xmlrpc4r library, found in Rubys standard library.

Heres 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 its 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 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 types

XML-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 e lucky) or a "fault" (if you e 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. Heres an example:

begin server.call( oSuchMethod) rescue XMLRPC::FaultException => e puts "Error: fault code #{e.faultCode}" puts e.faultString end # Error: fault code 7 # Can evaluate the expression because the name "noSuchMethod" hasn been defined.

Heres 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(18787765654) product[description] # => "Dr Bronners Peppermint Oil Soap" product[size] # => "128 fl oz" lookup_upc( o such UPC) # => nil

See Also

Категории