Your message has been sent.
This article has been saved to your account.
Go to my account
This article has been emailed to your Kindle.
Send this article
Complete the form below to send this article, Developing a REST based Web Service, to a friend (or to yourself). We will never share your details (or your friend's) with anyone. For more information, read our Privacy Policy.
REST (REpresentational State Transfer) is an architecture for distributed hypermedia systems. The World Wide Web is possibly the best known implementation of this architecture style. The term "REST" was coined and described by a dissertation written by Roy Fielding in 2000. This article by Nicholas Floyd covers the architecture which contains four basic constructs that address common concerns such as: scalability, generalized interfaces and resources, and patternized approaches for manipulation of resources.
Some of the problems and concerns that this architecture attempts to solve are:
- Security
- Follow common community standards / practices
- General interfaces
- Ease of consumption
- Fault tolerance
- Supports common resource representations
- Needs to scale and understand complex relationships
The four basic principles of REST
A REST based service should implement four basic constraints: identification of resources, manipulation or resources through, representations, self-descriptive messages, and HATEOAS (hypermedia as the engine of application state).
Identification of resources
Basically, any information that can be named can be a resource. A resource could be a representation of a code Class, a file on the file system, a simple block of text, a byte array of an image, or the image itself. Think of a resource as a conceptual representation of entity - a conceptual representation of a person, building, order, so on.
Given the non-virtual resource representing person, a conceptual representation of that resource might be:
<?xml version="1.0" encoding="utf-8"?>
<person id="" uri="">
<firstName/>
<middleName/>
<lastName/>
</person>
Given the non-virtual resource "John Adams", a representation of that resource might be:
<?xml version="1.0" encoding="utf-8"?>
<person id="123" uri="http://domain/people/123">
<firstName>John</firstName>
<middleName></middleName>
<lastame>Adams</lastame>
</person>
There are a few things that should be noted regarding resources:
- Resources can be highly static or highly dynamic
- Semantics of a resources mapping determines how "static" it is
- Static:
http://domain/People/123, Dynamic:http://domain/People/Search?status=new
- This abstract definition of a resource enables key features of the Web architecture to "work"
- Grouping of data without definition
- Late binding of the reference to a representation
- Allows reference to the resource concept without having to reference a specific resource
Manipulation of resources through representations
Some of the most basic constructs of HTTP 1.1 are some of the Web's most powerful tools. Users of the Web have been shielded from the specifics of protocols such as HTTP 1.1 (as described in RFC2616) via various user-agents such as Firefox and Google Chrome. It was no mistake that REST was being conceptualized at the same time HTTP 1.1 was being formulated. Both became instrumental to the success of Web based applications on the internet. To illustrate, consider the 7 basic URI routes for a Ruby on Rails application using the person representation from above:
|
HTTP Method |
URI |
Action |
|
GET |
people |
Index |
|
GET |
people/123 |
Show |
|
POST |
people |
Create |
|
PUT |
people/123 |
Update |
|
DELETE |
people/123 |
Delete |
|
GET |
people/123/new |
New |
|
GET |
people/123/edit |
Edit |
A patternized approach to URI schemes makes implementation and consumption of a REST service drastically more simplistic. The URI schemes above are examples of what the manipulation of resources through the use of HTTP verbs might look like. The point is that a RESTful service should provide the client with enough information to manipulate the resource.
Given the following: GET people/123
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Content-Location:http://domain/people/123
Date: Fri, 01 May 2009 05:36:38 GMT
Content-Length: 102
<?xml version="1.0" encoding="utf-8"?>
<person id="123" uri="http://domain/people/123">
<firstName>John</firstName>
<middleName></middleName>
<lastame>Adams</lastame>
</person>
The consumer of the REST service now has all they need to know to update the "middleName" node with the value of "Quincy":
PUT people/123
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Date: Fri, 01 May 2009 05:36:38 GMT
Content-Length: 102
<?xml version="1.0" encoding="utf-8"?>
<person id="123" uri="http://domain/people/123">
<firstName>John</firstName>
<middleName>Quincy</middleName>
<lastame>Adams</lastame>
</person>
Server responds:
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Location:http://domain/people/123
Date: Fri, 01 May 2009 05:36:40 GMT
Content-Length: 102
<?xml version="1.0" encoding="utf-8"?>
<person id="123" uri="http://domain/people/123">
<firstName>John</firstName>
<middleName>Quincy</middleName>
<lastame>Adams</lastame>
</person>
There are a few things to note regarding manipulation of resources via representations:
- A representation is a sequence of bytes plus representation metadata (in the form of name value pairs) to describe those bytes
- Data format = media type
- Composite media types can be used to encapsulate multiple representations in one message
Self-descriptive messages
This rule of REST implies that the server must provide enough information in each message, to the consumer, so that they can properly process the message. The message must somehow be self-descriptive so that the consumer can use it without having to look into the body of the message. Headers are probably the best example of how to inform the user without inspection. Using HOST headers, content-type, content-location, and even resource extensions the server implementation quickly enables successful consumption. Caching, chunking are also some of the other enabling mechanisms of HTTP 1.1.
There are a few things to note regarding self-descriptive messages:
- Messages may include both the representation metadata (ex. xsd), and the resource metadata (ex. nodes)
- Control data: action (get, put post, delete), or definition of the response
- Also use to override default behavior (supporting low and high rest clients)
HATEOAS (Hypermedia As The Engine Of Application State)
This constraint proposes that the server provides the client with enough information about the resource and all related resources via URIs and actions so that use and implementation are decoupled. Basically, the server will always hand the client proper URIs for all resources that are able to be referenced (at minimum). This is simply making media into hyper-media. HATEOAS attempts to reduce coupling and assumptions around hyper resources.
There are a few different degrees of a HATEOAS implementation, some are (but not limited to):
Simple (server response): Providing URIs for each resource
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Location:http://domain/people/123
Date: Fri, 01 May 2009 05:36:40 GMT
Content-Length: 102
<?xml version="1.0" encoding="utf-8"?>
<person id="123" uri="http://domain/people/123">
<firstName>John</firstName>
<middleName>Quincy</middleName>
<lastame>Adams</lastame>
<addresses uri="http://domain/people/123/addresses"/>
</person>
More complex (server response): Providing URIs for each resource, establishing custom content-types
Although the service provider can choose the implementation the API should attempt to provide an interface that is easy to understand and easy to use. Consider the following case for extending our people resource:
Given: The service provider has added a "goesByName" node to the xml being returned and received. The service provider has determined that this change has potential to break existing consumer interface contracts. Therefore, a new content-type will be created so that the existing consumers will not be impacted by the change
A good formula that can be used when determining what to name a versioned custom content type might be:
Content-Type: application/[custom vendor moniker].[domain].
[realm].[resource].[version]+[root media type]
- [custom vendor moniker]: The value for this is usually vnd, however this could be almost any notation. It is used to simply tell the consumer that this resource being used is a custom type that was generated by the service provider
- [domain]: Usually the service providers base domain, or API domain
- [realm]: This represents a logical grouping of data. Generally "realms" are used in APIs to either virtually (through code) or physically (through totally separate APIs) data. The reason that it might useful in the custom content type is so that the resources have a specific grouping. In the long run APIs that use realms tend to be able to implement more simplistic security models.
- [resource]: The actual resource that is being worked with. With our example this value could be people, address, communication, etc...
- [version]: It is recommended that this parameter be a simple version. Normal software versions can be and sometimes need to be complex (i.e. major.minor.branch.build or 1.3.2.6). This implementation should be more simplistic since the service provider should not be producing a multitude of different media types, the API is versioning resources not software, and complex version sequences are less meaningful to consumers
- [root media type]: This is the base type or format that is being returned xml, json, etc...
So, given out scenario above the "more complex" implementation might look like:
HTTP/1.1 200 OK
Content-Type: application/vnd.mydomain.people.people.v1+xml; charset=utf-8
Content-Location:http://domain/people/123
Date: Fri, 01 May 2009 05:36:40 GMT
Content-Length: 102
<?xml version="1.0" encoding="utf-8"?>
<person id="123" uri="http://domain/people/123" type=
"application/vnd.mydomain.people.people.v1+xml">
<firstName>John</firstName>
<middleName>Quincy</middleName>
<lastame>Adams</lastame>
<goesByName>JohnQ</goesByName>
<addresses uri="http://domain/people/123/addresses" type=
"application/vnd.mydomain.people.addresses.v1+xml"/>
</person>
Even more complex (server response): Providing URIs for each resource, establishing custom content-types, providing actionable semantics
HTTP/1.1 200 OK
Content-Type: application/vnd.mydomain.people.people.v1+xml; charset=utf-8
Content-Location:http://domain/people/123
Date: Fri, 01 May 2009 05:36:40 GMT
Content-Length: 102
<?xml version="1.0" encoding="utf-8"?>
<person id="123"
uri="http://domain/people/123"
type="application/vnd.mydomain.people.people.v1+xml"
submit="http://domain/people/123" method="POST">
<firstName>John</firstName>
<middleName>Quincy</middleName>
<lastame>Adams</lastame>
<goesByName>JohnQ</goesByName>
<addresses
uri="http://domain/people/123/addresses"
type="application/vnd.mydomain.people.addresses.v1+xml"
submit="http://domain/people/123/addresses" method="POST"/>
</person>
There are a few things to note regarding HATEOAS:
- Eliminates the need to the server to maintain an awareness of client state beyond the current request
- Provide a state machine through URIs in the resource
Other considerations
The service provider should consider HTTP status codes. There are a predefined set of codes that inform the consumer about the state of the request. These codes help reduce obscurity across web based implementations by requiring that the proper code be returned for a given response. Although these status codes are not all "required" the consumers will expect them and they will expect them to be implemented as defined in the RFC 2616. For instance, consider the following scenarios and the status codes that are returned:
Status code for a successful GET would normally be:
200 OK
- Entity body - The resource
- Example - [GET] People/1
Status code for a Create would normally be:
201 CREATED
- Response Header - Location will contain the canonical URI
- Entity body - The resource
- Example - [POST] People
Status code for a bad of malformed request would normally be:
400 BAD REQUEST
- Response Header - No modification
- Entity body - Optionally, information describing the failure
- Example - passing over a malformed xml document
Status code returned when a user attempts to access data that the service provider has not provided access to that user for would normally be:
403 FORBIDDEN
- Response Header - No modification
- Entity body - explanation of why authorization failed, or why the user cannot access the requested resource
Status code returned when a consumer application tries to request a specific resource that does not exist:
404 NOT FOUND
- Response Header - No modification
- Entity body - should be empty
- Example - [GET] People/1 does not exist, optionally a 410 may be used (see below)
Status code returned when a consumer application tries to request a specific resource as a specific media type that the service provider does not support (this status code provides the greatest help when a service provider has implemented some form of HATEOAS using custom media types):
415 UNSUPPORTED MEDIA TYPE
- Response Header - Code detailed supported media types for the given resource
- Entity body - should be empty
- Example - Resource only supports XML and JSON, but the client sends application/atom+xml
The service provider should also consider HTTP headers. Request and response headers are instrumental to making web applications work, so they should be considered extremely important in API design and implementation. Although some headers are not "required" the more information and help that the API can provide to consumers the better. For instance, consider the following response and request headers:
Request Headers
Accept : type/subtype
- Example - Accept : application/xml
- Notes - Defaults to application/xml if one is not passed in, and when a user sends application/* the resulting content sub type will be XML. Multiple types/sub types can be sent in but as per the specification the first match in degree of specificity will be the first used.
i.e. if user sends in: application/*, application/xml, / the order would be as follows:- application/xml
- application/*
- */*
- Acceptable values - application/xml, application/json, application/xsd, application/help
- If an invalid value is passed in via Accept header the response will result in a 415 Unsupported Media Type
Accept-Charset : charset
- Example - Accept-Charset: utf-8
- Notes - Accept-Charset will not be used and will always default to utf-8
Accept-Encoding : encoding
- Example - Accept-Encoding : deflate
- Notes - If none is sent in then the API will default to no compression.
- Acceptable values - deflate, compress, gzip
- If an invalid value is passed in via Accept-Encoding header the response will result in a 406 Not acceptable
Date : HTTP-date
- Example - Date: Thu, 29 Jan 2009 15:28:25 GMT
- Notes - Optional, most commonly passed in with PUTs and POSTs
Response Headers
Allow : method
- Example - Allow: GET, PUT
- Notes - An Allow header field MUST be present in a 405 (Method Not Allowed) response.
- Return values - GET, PUT, POST, DELETE
Content-Encoding : content-coding
- Example - Content-Encoding : gzip
- Notes - This will tell the client what type of compression was used on the resource
- Return values - deflate, compress, gzip
Content-Length : DIGIT
- Example - Content-Length : 1254
- Notes - Sent back with each request. Will possibly be available via HEAD requests
Content-Location : absoluteURI | relativeURI
- Example - Content-Location : http://domain/v1/people/1234
- Notes - Sent back with each GET request
Content-Type : media-type
- Example - Content-Type : application/xml, utf-8
- Notes - Details the type of content being returned to the client
Date : HTTP-date
- Example - Date: Thu, 29 Jan 2009 15:28:25 GMT
- Notes - Will be returned with every response, possibly excluding responses returning status codes of 500
Location : absoluteURI
- Example - Location: http://domain/v1/people/1234
- Notes - Applies to 201 and 301 only
Summary
The RESTful approach to web services is ideal in a web based consumer environment; where consumers can leverage the protocols that make web architecture possible. Specifically the HTTP 1.1 specification (RFC2616) should be considered and followed in any REST based service. Key elements defined by that protocol such as status codes and HTTP headers help the service provider to extend the service without compromising the implementation while lowering knowledge barriers around consumer usage.
If you have read this article you may be interested to view :
About the Author :
Nicholas Floyd is the Integration Architect at Fellowship Technologies where he builds and creates APIs and integration points for all types of systems. He has been working in web based software for 15 years developing a myriad of web applications and sites. He's married, father of 3 awesome boys, and a guy who loves God! Links to his articles may be found at his website nicholasfloyd.com and can be contacted via twitter @nickfloyd.




Post new comment