Friday, October 30, 2009

Recipe 16.3. Writing an XML-RPC Client










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 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'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