On a current project, I needed to develop a series of web services for a custom single-signon (unified login) for a bunch of different websites to share. The project needed to be in Ruby on Rails, since that is what is available to the servers, and needed to use a protocol which PHP, Java and Ruby could all understand.
At first I tried to
investigate using ActiveResource, but I found this to be excessively
Rails-centric, and it only seemed to provide basic CRUD functionality. I
needed these webservices to do a lot more work, with a lot more
parameters. Since the Rails community (and a lot of web developers in
general) seem to rave about RESTful services, my next direction was to
write a custom series of REST-based webservices. As I proceeded, it
became glaringly obvious that REST services have on major shortcoming –
complex data.
Since the basic idea of REST is that all parameters become part of the URI itself (something, I might add, ActiveResource violates right away), you immediately have a problem when it comes to things like street addresses. Everyone’s solution is to use basic HTTP parameters or to URL-encode the data, but to me these solution tainted the point of REST, and made for some truly hideous looking URL’s, respectively.
I also didn’t like the lack of type control. This same lack also negated using JSON-RPC or even some custom YAML-based solution. All the experiences I had with SOAP left a bad taste in my mouth for that one, so what was I to do?
Thankfully, I discovered that Ruby had XML-RPC functionality built right in. But the problem arose that all examples I could find of using it (since Ruby’s documentation is complete and utter dog-sh*t) only showed the XML-RPC server running in stand-alone mode. I certainly couldn’t do this. So after much tinkering, I herewith present a controller class which you can extend to offer very simple XML-RPC calls from within a Rails environment:
# # This class provides a framework for XML-RPC services on Rails # require 'xmlrpc/server' class WebServiceController < ApplicationController # XML-RPC calls are not session-aware, so always turn this off session :off def initialize @server = XMLRPC::BasicServer.new # loop through all the methods, adding them as handlers self.class.instance_methods(false).each do |method| unless ['index'].member?(method) @server.add_handler(method) do |*args| self.send(method.to_sym, *args) end end end end def index result = @server.process(request.body) puts "\n\n----- BEGIN RESULT -----\n#{result}\n----- END RESULT -----\n" render :text => result, :content_type => 'text/xml' end end
Here is a working example of using the above code:
class StringController < WebServiceController def upper_case(s) s.upcase end def down_case(s) s.downcase end end
Invoking the remove method couldn’t be any simpler:
require 'xmlrpc/client' require 'pp' server = XMLRPC::Client.new2("http://localhost:3010/string") result = server.call("upper_case", "This is my string") pp result result = server.call("down_case", "This is my string") pp result
I certainly hope this simple bit of Ruby will help anyone else who may have suffered trying to figure this out as I have.
Enjoy!
Awesome, using it as we speak! Two questions: 1) Is there a way, using xmlrpc/server, to create a method with variable # arguments? Like method1(str, *)? 2) What do you think of using just format.xml instead of xmlrpc? Would this loose the ability to send large and complex data? See http://stackoverflow.com/questions/1967194/is-there-an-example-of-using-activeresource-and-xmlrpc-for-rails/1967249#1967249
very usefull information, thanks a lot