updated: 2007-02-20T23:49:26.412978 author: title: REST and WS-* slug: REST-and-WS published: 2007-02-16T22:51:19.495076 id: 125
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.
Update: Added a more detailed section on self-descriptive messages.
Update 2: Moved large chunks of the document around based on feedback. I moved the 'self-describing' section earlier in the document and tried to improve the transitions between the sections. I also added a TOC.
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.
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:
So far we've seen how switching from serializing objects to exchanging documents helps with platform neutrality, but where do the benefits of scalability and performance come from? For that we'll have to delve into the "self-descriptive" nature of RESTful messages.
At this point you may have the idea that RESTful has to do entirely with formats and serializations, which isn't true.
To explain, a bit more needs to be said about self-descriptive messages.
This is a rather fundamental principle and is the reason that RESTful systems can scale much easier. This has to do with the amount of information that each message carries and that is available to intermediaries without peeking into the body. That's a critical distinction since parsing and processing headers is something every intermediary has to do, while no intermediary is required to be able to parse any of the bodies that pass through it.
Here is a simple HTTP request. Note all the information present in the headers:
GET /text/12 HTTP/1.1
User-Agent: SomeBloggingClient
Host: example.org
Accept: text/html
If-None-Match: "234902340932423432"
This message is self-descriptive, it uses a standard method with known semantics (GET, which is safe and idempotent), a well-known media-type (text/html), and it also declares how cached representations of this resource are to be handled (Revalidate with the origin server using the supplied entity tag.).
An intermediary can process these messages in a helpful way because the safety, idempotency, cacheability, and well-known media-types are all apparent from the message headers (and request-line and status line). The self-descriptive nature of the requests and responses are critical, for example, to accelerator intermediaries that keep a nearby cache, compress content, or down-sample images to make them smaller and thus faster to transfer. The intermediaries that down-sample images are possible because of a constrained set of media-types, i.e. there are only so many image formats widely used.
You may ask at this point if SOAP requests, which also pass over HTTP, are self-descriptive? You are correct in that a SOAP request and response are both self-descriptive, but if we go back and look at old POST-only SOAP we see that it is actually the least self-descriptive message you could construct, (Yes, later versions of SOAP allow GET, and I've already pointed out where WS-* has it's advantages, this is just a more detailed explaination of why RESTful messages scale, and hopefully by the end of this explaination you will see how just adding GET doesn't move the bar very far.)
Here is a sample SOAP request:
POST /examples HTTP/1.1
User-Agent: Radio UserLand/7.0 (WinNT)
Host: localhost:81
Content-Type: text/xml; charset=utf-8
Content-length: 474
SOAPAction: "/examples"
<?xml version="1.0"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">
<SOAP-ENV:Body>
<m:getStateName xmlns:m="http://www.soapware.org/">
<statenum xsi:type="xsd:int">41</statenum>
</m:getStateName>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
In a SOAP request everything is a POST, which in this case has the very generic meaning of "process this", so intermediaries lose the ability to distinguish safe and idempotent requests. In addition, using POST everywhere means that all cachability is lost. A very vanilla media-type of "text/xml" is also used, again reducing the chances of intermediaries being able to do any helpful processing. And finally many kinds of requests will all go through the same SOAP server URI, which is another limiting factor on intermediaries, such as reverse proxies, which can be used to distribute load.
So while SOAP requests and responses are self-descriptive, they carry a miniscule amount of information that intermediaries might be able to work with, and if intermediaries can't help then scalability will be limited.
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?
The best way to describe how to build a RESTful interface 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.]
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.
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.
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": "jcg111002222”
},
{
"name": "John Q. Public",
"href": "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, as a relative URI, at the "href" key. 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. Note that I could have just as easily supplied an absolute URI.
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 |
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.
There are aspects of REST that have not been covered yet, and they include:
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 simple links. While WSDL can be congratulated for adding URLs as first class citizens, it 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. This isn't a minor quibble, following links from document to document is a fundamental aspect of the web. The simple <a> tag, which can appear in any tag soup HTML page, is the core of the PageRank algorithm, the basis for the market cap of Google, and completely outside the reach of WSDL 2.0.
One immediate result of not understanding links is that you can't write a WSDL document for the Atom Publishing Protocol. There are other RESTful systems that can't be described and they're listed below to give an idea of the limitations of WSDL 2.0.
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.