In a large system you may be faced with either a multitude of
clients or a menagerie of them; in either case you have to stop serializing
objects and start exchanging documents.
Before comparing REST and WS-* and outlining where each of them are best
used, I need to first define some terms. There is substantial technical and
marketing literature available that I believe WS-* is self-apparent.
Defining REST will take some more work since there is substantial hype and
many things on the web called REST are not RESTful at all. The problem is
that REST is not a specific piece of technology but an Architectural Style
that was abstracted from HTTP during the transition from HTTP 1.0 to HTTP
1.1. At the time there were major performance issues being grappled with and
HTTP 1.1 was designed with this model as a helper to address those
performance and scaling concerns. The upshot is that HTTP does not have
everything that REST indicates should be present, and there is the
additional problem that while HTTP is the first, and best, implementation of
REST, the two are not the same and yet are often confused. While REST is
best thought of as a guide and not a law, you have to remember that so is,
"Don't run with scissors".
For the remainder of this paper when I refer to REST I'll be talking about HTTP used as RESTfully as possible.
The Right Tool
REST and WS-* are two different tools whose strengths shine at different scales.
The easiest way to think about this is an example from nature: at the scale of
the atom the forces responsible for most of the action are different from the
forces at the scale of a cell. Quantum effects and the strong nuclear force
determine the structure and operation of an atom, while the operation of a cell
is dominated by molecular reactions and Van der Waals' forces.
Another example closer to home; when programming and making calls into other
functions and libraries, you pass along classes and types in the function call
parameters. You expect those classes and types to be perfectly understood on the
other side of that function call. Those are the rules at that scale; that type
information can be counted on to survive and be useful over the function call
boundary. As your scale grows, as you move outside the single executable, the
same machine, or the same platform, that assumption begins to weaken, to the
point that when you get to Internet scale services that assumption is actually
harmful.
When working at the smaller scale the assumption that types can move across a
boundary is powerful and allows many optimizations. Working in a homogeneous
environment such as Java, WS-* has real advantages; you can very quickly create
interfaces in your target programming language and expose those interfaces via
WSDL and have them consumed just as easily on the calling side using the same
WSDL. This is illustrated in Figure 1, where the focus is moving objects between
homogeneous systems.
Figure 1
Moving object “A” from system to system.
As you move to larger systems, either many more clients connecting, or a
non-homogeneous pool of clients, this paradigm starts to break down. If there
are many clients then the demands for caching semantics will be begin to
dominate. In that case you need to abandon HTTP as just a simple transport and
start using the application level semantics of HTTP to start leveraging the
caching architecture already built into the Internet.
In the case of a non-homogeneous pool of clients you run into the fact that all
those other languages aren't just
Java-without-the-compile,
they are very different systems with radically differing views on typing, classes and
type-safety. As you move into such a world you cannot get your object “A” from
system to system. Instead you have to switch your focus to the bits on the wire,
and leave it up to each client system how to best interpret that data. That's
illustrated in Figure 2, where the format “A” is defined and each client
interprets that document in an optimal manner for that environment.
Figure 2
Defining the format “A” which is interpreted according to local custom in each environment.
While defining formats and leveraging HTTP as your application protocol takes
more time, and thus incurs a cost, you get benefits from paying that cost:
- Scalability
- Performance
- Platform neutrality
- Leveraging Internet scale infrastructure
In a large system you may be faced with either a multitude of clients or a
menagerie of them; in either case you have to stop serializing objects and start
exchanging documents.
So if you aren't going to use classes and types and RPC to define
your interface, how are you supposed to create a RESTful interface?
REST Recipe
The best way to describe REST is to give a recipe for how to build such a system, a heuristic for how to model your system RESTfully.
[For a more indepth article see How to create a REST protocol.]
- Find all the nouns
- Define the formats
- Pick the operations
- Highlight exceptional status codes
As a simple example let's build a RESTful interface for managing a list of
employees. Each employess has a name, title, and contact information and we want
to be able to add, remove, and update employee information as well as get a list
of all the employees.
Find all the nouns
Everything in a RESTful system is a noun, a Resource, and every noun is identified by a URI.
In our employee system there are two nouns, two types of resources, the employee, and the list of all employees. Let's define the URIs for each of those resources:
| Resource | URI |
| Employee List | /employees/ |
| Employee | /employees/{employee-id} |
Where {employee-id} will be substituted with the employees identification number.
Define the formats
Now we need to define the formats, or the representations, for each of these
resources. We'll use JSON for the format. Here is an example representation
of an employee:
{
"name": "Joe Gregorio",
"title": "Software Engineer",
"contact": {
"address1": "123 AnyStreet",
"city": "Sometown",
"state": "NC”
}
}
Figure 3
Employee JSON Representation
Here is an example of the employee list:
[
{
"name": "Joe Gregorio",
"href": "http://example.org/employees/jcg111002222”
},
{
"name": "John Q. Public",
"href": "http://example.org/employees/jqp333445555”
},
...
]
Figure 4
Employee List JSON Representation
Updating our table:
| Resource | URI | Representation |
| Employee List | /employees/ | JSON (emp list |
| Employee | /employees/{employee-id} | JSON
(employee) |
Note that in the employee list each entry contains the URI of the individual
employee. This is an aspect of REST that we have skimmed over lightly, the use
of hyper-text as the engine of application state. While you could just stick the
employee id in there and then count on the client to be able to construct the
URI of the employee resource from that information, its easier on the client,
opens more avenues for optimization, and reduces coupling if the URI is passed
along directly.
Pick the operations
Now that we have our resources and representations we define the operations that
we want to perform on those resources. To retrieve a representation of a
resource we use GET, to update we use PUT, and to remove it we use DELETE. POST
can be used to either have some generic processing done, or it can be used to
create new resources. To create a new employee we will POST an employee JSON
document to the employee list.
| Resource | URI | Method | Representation | Description |
| Employee List | /employees/ | GET | JSON (emp list) | Retrieve the list of employees |
| POST | JSON (employee) | Create a new employee |
| Employee | /employees/{employee-id} | GET | JSON (employee) | Retrieve an employee |
| PUT | JSON (employee) | Update an employee |
| DELETE | - | Remove an employee |
Highlight important status codes
Now our service is almost completely described. As a convenience for client
developers we can highlight certain status codes that are important. For
example, when an employee is successfully created the server will return an HTTP
status code of 201 and the response will include a Location: header with the URI
of the newly created employee resource.
HTTP Semantics
There are useful semantics available in HTTP. For example, HTTP supports ETags.
An ETag, or entity-tag, is used to compare two representations from the same
resource. The easiest way to think of them as a hash of all the bytes in a
representation, an opaque string that changes every time a representation
changes even by one bit. Entity-tags are used in GET requests as cache
validators. That is, if I do an initial GET on a resource I may get an ETag
returned with that response. On subsequent requests to the same resource I can
send that entity-tag in an If-None-Match: header. If that resource has not
changed then its entity-tag is still the same and the server will return a 304
response with no entity-body, which can be a huge traffic savings. If the
resource has changed then the entity-tag will not match and the new, modified
representation will be returned in the response.
But the use of entity tags extends beyond just GET. If I later want to update
that resource, and have built my system RESTfully then I will PUT my updated
representation back to the same URI. In that case I can put the entity-tag in an
If-Match: header. Only if the entity-tags match will the PUT succeed. This
prevents the lost-update problem, where another client comes along and modifies
the resource between your GET and PUT.
RESTful aspects not covered
There are several aspects of REST that have not been covered yet.
- Code on demand
-
One is the idea of code on demand. It is perfectly RESTful for a web browser to
get fed JavaScript and to have that JavaScript then execute in the browser and
perform other RESTful calls.
- Constrained set of operations
- REST calls for a restricted set of methods and in the exercise above we only use
GET, PUT, POST and DELETE. While much can be done with them, we aren't
restricted to using only those four. HEAD is another useful method that should
be supported anywhere GET is supported. In addition there is room for other
methods, such as PATCH, as long as the semantics for how those methods are
applied to a resource are uniform.
- Self-descriptive messages
-
Self-descriptive is another aspect of REST that was briefly touched upon. Each
time we send or receive a JSON representation in our example, the Content-Type
header is also sent with a value of 'text/json'. In this way each message is
self-descriptive, there is a method sent, and that is applied to a resource
whose URI is also sent in the request, and the type of the request or response
entity is also included in the headers. REST allows intermediaries to process
requests and responses because each message is self-descriptive. This allows
the introduction of caches, proxies, and other intermediaries to help
performance and scalability.
WSDL 2.0
It's useful and informative to ask if RESTful services can be described in WSDL
2.0. I'll only cover the ability of WSDL 2.0 to describe such a service and
leave the unearthing of WSDL 2.0 compliant implementations as an exercise for
the reader.
At first brush the HTTP Binding Extension for WSDL 2.0 looks like it might be a
good REST description language when you see reference to the methods GET, PUT,
POST and DELETE; and references to HTTP header construction. Digging in further
reveals the constraints have been only slightly loosened and WSDL 2.0 is still
about serializing objects, not exchanging documents.
From Section 6.3.2 of WSDL 2.0 Part 2: Adjuncts:
If the Interface Message Reference component or the Interface Fault component is
declared using a non-XML type system (as considered in the Types section of
[WSDL 2.0 Core Language]) then additional binding rules MUST be defined to
indicate how to map those components into the HTTP envelope.
The upshot is that you can't do JSON without writing a separate specification
for binding rules on how to serialize “text/json”. The same holds true for any
binary format, such as GIF and JPEG. There are other explicit and implicit
limitations in the HTTP Binding for WSDL 2.0, such as the requirement for XML
Schema, even in the case where data is serialized as
“application/x-www-form-urlencoded”. Another telling example is the limiting of
HTTP authentication methods to Basic and Digest. HTTP includes in RFC 2617 not
only the definition of Basic and Digest authentication, it also defines an
extensible mechanism by which a client and server negotiate authentication
methods. This is an extensible system wherein the server returns a list of
acceptable authentication mechanisms and the client chooses from that list of
challenges. Limiting the number of authentication mechanisms to just those two
cripples such an extension mechanism. In general most of these problems stem
from the same root cause: RESTful HTTP messages are self-descriptive. It is that
self-descriptive aspect of HTTP messages that allows generic processors such as
proxies and caches to be built, so any language that tries to describe HTTP
messages must either include the whole HTTP specification or introduce
constraints that don't exists in HTTP.
One last area to highlight is the lack of support for “hypertext as the engine
of application state”. This is just a way of stating that WSDL, while it can be
congratulated for adding URLs as first class citizens, doesn't understand links
as such first class citizens. That is, there is no mechanism in WSDL 2.0 to
identify new resources that are identified by links in other documents.
Following links from document to document is a fundamental aspect of the web,
and one result of not understanding that links to new resources can appear in
documents is that you can't write WSDL for the Atom Publishing Protocol.
The best way to demonstrate the lack of RESTfulness in WSDL 2.0 would be list
some examples of things you can't do in WSDL 2.0:
- AJAX with JSON
-
Response bodies in WSDL 2.0 can't be anything but XML without defining a new
binding, and the only two other types of request bodies that are supported are
“application/x-www-form-urlencoded” and “multipart/form-data”. That means you
can't send JSON to a service, not can your service return JSON, both of which
are staples of contemporary rich Internet applications.
- Atom Publishing Protocol
-
The Atom Publishing Protocol can't be described in WSDL 2.0 for two reasons, the
first being that Atom Documents can't be fully described with XML Schema. The
second is that WSDL 2.0 has no concept of hypertext, and navigating collections
using URLs in Atom Feeds is a fundamental technique in the Atom Publishing
Protocol.
- GData
-
Google has a restricted subset of the Atom Publishing Protocol that they have
defined as the interface into services such as Blogger, Spreadsheets, and
Calendar. GData can't be described in WSDL 2.0 because, in addition to the
general problems we already listed for APP implementations, it has the
additional problem that Google has defined their own HTTP level authentication,
which conforms to the extension model defined in RFC 2617, but is neither Basic
nor Digest.
None of this is to detract from WSDL 2.0 when it is applied in the right problem domain,
this is just pointing out that it is currently incomplete for describing RESTful
services.