3.1. REST-Based
Web Service Server Serving Proprietary XML Documents
RESTfor our purposesis really the same thing as
a simple web GET request: it requires no special server setup or
request encoding and decoding. You simply use your favorite web
server and serve your results in whatever format you choose to
designusually a custom XML format. Regardless of the implementation
language, most developers agree that REST services are simple to
build and flexible enough to adapt to your needs. Additionally, if
you closely adhere to the true intent of REST, you can take
advantage of any HTTP-based filters and security measures your
server has in place. Because of these types of benefits, and REST's
simplicity, most of the web services out in the "real world" are
REST services, rather than SOAP or XML-RPC services.
When it comes to Rails applications, RXML
templates help make it simple to build REST-based servers with
custom XML documents. In fact, to build REST-based web servers with
Ruby on Rails , you really only need to:
-
Make sure you turn off layouts for the methods
you'll be using in your service
-
Associate RXML templates with the methods in
your controllers, instead of RHTML templates
Let's build a simple service to demonstrate.
Here's a controller with some dummy data. Update your existing
code_controller.rb file from the
previous examples to contain this new restserver method
and layout command:
class CodeController < ApplicationController
layout "application", :except => [:restserver]
def restserver
@sampledata = ["Kevin","Timothy","Catherine"]
end
end
Here's the view associated with the controller;
save it as restserver.rxml in the
app/views/code directory):
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
xml.exampledata do
counter = 0
@sampledata.each do |rec|
counter = counter + 1
xml.name(rec, :id => counter)
end
end
And just like that, your simple REST-based
server is done! REST clients can access the service at the URL
http://localhost:3000/code/restserver.
This example used one additional Ruby library:
the Builder library. According to its documentation, the Builder
library "provides a simple way to create XML markup and data
structures." It gets installed with Rails by default, but if you
want to make sure you have the latest version, you can use the
gem command:
gem install builder
Complete documentation on the Builder library
can be found at builder.rubyforge.org
The controller for our application is very
simplein fact, too simple. In real life, the controller would
include other actions associated with a "regular" web site. So
let's assume those methods exist and that they use standard RHTML
templates. We need to disable the layout, but only for those
actions that are part of the REST service (the restserver
action). So we call the layout method with the
except parameter:
layout "application", :except => [:restserver]
We've disabled the layout, but what about any
action-specific templates? RHTML templates take precedence over
RXML templates. If you have both restserver.rhtml and restserver.rxml in your views/code directory, restserver.rhtml will be used for rendering.
So it's important to be careful about your file extensions when
building REST-based services.
That's about all there is to the controller; the
restserver method just packs an instance variable with
data that the view will put into the XML document.
The view (in this case, an RXML template) uses
the Builder library to build an XML document. Rails' ActionPack
library knows to use the Builder library to render this view
because of the RXML extension. Using Builder to generate your XML
is straightforward. Since we want our document to be a true XML
document, we start by calling the xml.instruct method,
where xml is an XML document object that's available in
any RXML template:
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
To add tags to the document, we call methods on
the xml object. The method name is the name of the tag.
The tag's content (the text between the beginning and ending tags)
is the first parameter to this method. Pass any attributes for this
tag as hash values. Here's a very simple example:
xml.name("Kevin", :id => 1, "age" => "31")
# creates <name id="1" age="31">Kevin</name>
In a real document, of course, you need to deal
with nested tags. You can nest tags by defining the children tags
inside the block of the parent tag:
xml.exampledata do
xml.name("Kevin")
end
# creates <exampledata><name>Kevin</name></exampledata>
Additional details and complete documentation on
the Builder library can be found online at builder.rubyforge.org.
As you can see, building REST web services
really is straightforward. But you still need to do a lot of grunt
work: you need to create an XML document, then tell your clients
how to parse your proprietary documents and how to invoke your
service. This is one of the main reasons SOAP and XML-RPC were
created: to give web services a standard for communication between
the server and client. SOAP and XML-RPC allow developers to build
clients without spending a lot of time learning how to communicate
with the server for each new service. So let's shift focus to
building SOAP and XML-RPC servers.
|