REST fundamentals
REST resources and their representations
HTTP methods and status codes
Richardson’s Maturity Model
Today, the Web has become an integral part of our lives—from checking statuses on Facebook to ordering products online to communicating via email. The success and ubiquity of the Web have resulted in organizations applying the Web’s architectural principles to building distributed applications. In this chapter, we will take a deep dive into REST, an architectural style that formalizes these principles.
What Is REST?
Client-server—Concerns should be separated between clients and servers. This enables client and server components to evolve independently and in turn allows the system to scale.
Stateless—The communication between client and server should be stateless. The server need not remember the state of the client. Instead, clients must include all of the necessary information in the request so that the server can understand and process it.
Layered system—Multiple hierarchical layers such as gateways, firewalls, and proxies can exist between client and server. Layers can be added, modified, reordered, or removed transparently to improve scalability.
Cache—Responses from the server must be declared as cacheable or noncacheable. This would allow the client or its intermediary components to cache responses and reuse them for later requests. This reduces the load on the server and helps improve the performance.
Uniform Interface—All interactions between client, server, and intermediary components are based on the uniformity of their interfaces. This simplifies the overall architecture as components can evolve independently as long as they implement the agreed-on contract. The Uniform Interface constraint is further broken down into four subconstraints: resource identification, resource representations, self-descriptive messages, and Hypermedia as the Engine of Application State, or HATEOAS. We will examine some of these guiding principles in the later sections of this chapter.
Code on demand—Clients can extend their functionality by downloading and executing code on demand. Examples include JavaScript scripts, Java applets, Silverlight, and so on. This is an optional constraint.
Applications that adhere to these constraints are considered to be RESTful. As you might have noticed, these constraints don’t dictate the actual technology to be used for developing applications. Instead, adherence to these guidelines and best practices would make an application scalable, visible, portable, reliable, and able to perform better. In theory, it is possible for a RESTful application to be built using any networking infrastructure or transport protocol. In practice, RESTful applications leverage features and capabilities of the Web and use HTTP as the transport protocol.
The Uniform Interface constraint is a key feature that distinguishes REST applications from other network-based applications. Uniform Interface in a REST application is achieved through abstractions such as resources, representations, URIs, and HTTP methods. In the next sections, we will look at these important REST abstractions.
Understanding Resources
The key abstraction of information in REST is a resource.
—Roy Fielding
Fundamental to REST is the concept of resource. A resource is anything that can be accessed or manipulated. Examples of resources include “videos,” “blog entries,” “user profiles,” “images,” and even tangible things such as persons or devices. Resources are typically related to other resources. For example, in an ecommerce application, a customer can place an order for any number of products. In this scenario, the product resources are related to the corresponding order resource. It is also possible for a resource to be grouped into collections. Using the same ecommerce example, “orders” is a collection of individual “order” resources.
Identifying Resources
The scheme and the scheme-specific-part are separated using a semicolon. Examples of a scheme include http or ftp or mailto and are used to define the semantics and interpretation of the rest of the URI. Take the example of the URI—http://www.apress.com/9781484208427. The http portion of the example is the scheme; it indicates that an HTTP scheme should be used for interpreting the rest of the URI. The HTTP scheme, defined as part of RFC 7230,2 indicates that the resource identified by our example URI is located on a machine with host name apress.com.
URI and Resource Description
URI | Resource description |
---|---|
http://blog.example.com/posts | Represents a collection of blog post resources. |
http://blog.example.com/posts/1 | Represents a blog post resource with identifier “1”; such resources are called singleton resources. |
http://blog.example.com/posts/1/comments | Represents a collection of comments associated with the blog entry identified by “1”; collections such as these that reside under a resource are referred to as subcollections. |
http://blog.example.com/posts/1/comments/245 | Represents the comment resource identified by “245.” |
Even though a URI uniquely identifies a resource, it is possible for a resource to have more than one URI. For example, Facebook can be accessed using URIs https://www.facebook.com and https://www.fb.com. The term URI aliases is used to refer to such URIs that identify the same resources. URI aliases provide flexibility and added convenience such as having to type fewer characters to get to the resource.
URI Templates
When working with REST and a REST API, there will be times where you need to represent the structure of a URI rather than the URI itself. For example, in a blog application, the URI http://blog.example.com/2014/posts would retrieve all the blog posts created in the year 2014. Similarly, the URIs http://blog.example.com/2013/posts, http://blog.example.com/2012/posts, and so forth would return blog posts corresponding to the years 2013, 2012, and so on. In this scenario, it would be convenient for a consuming client to know the URI structure http://blog.example.com/year/posts that describes the range of URIs rather than individual URIs.
URI templates, defined in RFC 6570 (http://tools.ietf.org/html/rfc6570), provide a standardized mechanism for describing URI structure. The standardized URI template for this scenario could be
http://blog.example.com/{year}/posts
The curly braces {} indicate that the year portion of the template is a variable, often referred to as a path variable. Consuming clients can take this URI template as input, substitute the year variable with the right value, and retrieve the corresponding year’s blog posts. On the server side, URL templates allow the server code to parse and retrieve the values of the variables or selected portions of URI easily.
Representation
RESTful resources are abstract entities. The data and metadata that make a RESTful resource need to be serialized into a representation before it gets sent to a client. This representation can be viewed as a snapshot of a resource’s state at a given point in time. Consider a database table in an ecommerce application that stores information about all the available products. When an online shopper uses their browser to buy a product and requests its details, the application would provide the product details as a web page in HTML. Now, when a developer writing a native mobile application requests product details, the ecommerce application might return those details in XML or JSON format. In both scenarios, the clients didn’t interact with the actual resource—the database record-holding product details. Instead, they dealt with its representation.
REST components interact with a resource by transferring its representations back and forth. They never directly interact with the resource.
Postfixing the URI with the desired representation—In this strategy, a client requesting product details in JSON format would use the URI http://www.example.com/products/143.json. A different client might use the URI http://www.example.com/products/143.xml to get product details in XML format.
Using the Accept header—Clients can populate the HTTP Accept header with the desired representation and send it along with the request. The application handling the resource would use the Accept header value to serialize the requested representation. The RFC 26163 provides a detailed set of rules for specifying one or more formats and their priorities.
JSON has become the de facto standard for REST services. All of the examples in this book use JSON as the data format for requests and responses.
HTTP Methods
The “Uniform Interface” constraint restricts the interactions between client and server through a handful of standardized operations or verbs. On the Web, the HTTP standard4 provides eight HTTP methods that allow clients to interact and manipulate resources. Some of the commonly used methods are GET, POST, PUT, and DELETE. Before we delve deep into HTTP methods, let’s review their two important characteristics—safety and idempotency.
The HTTP specification uses the term method to denote HTTP actions such as GET, PUT, and POST. However, the term HTTP verb is also used interchangeably.
Safety
A HTTP method is said to be safe if it doesn’t cause any changes to the server state. Consider methods such as GET or HEAD, which are used to retrieve information/resources from the server. These requests are typically implemented as read-only operations without causing any changes to the server’s state and, hence, considered safe.
Safe methods are used to retrieve resources. However, safety doesn’t mean that the method must return the same value every time. For example, a GET request to retrieve Google stock might result in a different value for each call. But as long as it didn’t alter any state, it is still considered safe.
In real-world implementations, there may still be side effects with a safe operation. Consider the implementation in which each request for stock prices gets logged in a database. From a purist perspective, we are changing the state of the entire system. However, from a practical standpoint, because these side effects were the sole responsibility of the server implementation, the operation is still considered to be safe.
Idempotency
An operation is considered to be idempotent if it produces the same server state whether we apply it once or any number of times. HTTP methods such as GET, HEAD (which are also safe), PUT, and DELETE are considered to be idempotent, guaranteeing that clients can repeat a request and expect the same effect as making the request once. The second and subsequent requests leave the resource state in exactly the same state as the first request did.
Consider the scenario in which you are deleting an order in an ecommerce application. On successful completion of the request, the order no longer exists on the server. Hence, any future requests to delete that order would still result in the same server state. By contrast, consider the scenario in which you are creating an order using a POST request. On successful completion of the request, a new order gets created. If you were to re-“POST” the same request, the server simply honors the request and creates a new order. Because a repeated POST request can result in unforeseen side effects, POST is not considered to be idempotent.
GET
The GET method is used to retrieve a resource’s representation. For example, a GET on the URI http://blog.example.com/posts/1 returns the representation of the blog post identified by 1. By contrast, a GET on the URI http://blog.example.com/posts retrieves a collection of blog posts. Because GET requests don’t modify server state, they are considered to be safe and idempotent.
In addition to the representation, the response to GET requests includes metadata associated with the resource. This metadata is represented as a sequence of key value pairs called HTTP headers. Content-Type and Server are examples of the headers that you see in this response. Because the GET method is safe, responses to GET requests can be cached.
The simplicity of the GET method is often abused, and it is used to perform operations such as deleting or updating a resource’s representation. Such usage violates standard HTTP semantics and is highly discouraged.
HEAD
On occasions, a client would like to check if a particular resource exists and doesn’t really care about the actual representation. In another scenario, the client would like to know if a newer version of the resource is available before it downloads it. In both cases, a GET request could be “heavyweight” in terms of bandwidth and resources. Instead, a HEAD method is more appropriate.
Like GET, the HEAD method is also safe and idempotent and responses can be cached on the client.
DELETE
The DELETE method, as the name suggests, requests a resource to be deleted. On receiving the request, a server deletes the resource. For resources that might take a long time to delete, the server typically sends a confirmation that it has received the request and will work on it. Depending on the service implementation, the resource may or may not be physically deleted.
On successful deletion, future GET requests on that resource would yield a “Resource Not Found” error via HTTP status code 404. We will be covering status codes in just a minute.
Because DELETE method modifies the state of the system, it is not considered to be safe. However, the DELETE method is considered idempotent; subsequent DELETE requests would still leave the resource and the system in the same state.
PUT
The PUT method allows a client to modify a resource state. A client modifies the state of a resource and sends the updated representation to the server using a PUT method. On receiving the request, the server replaces the resource’s state with the new state.
Consider the case in which we just wanted to update the blog post title. The HTTP semantics dictate that as part of the PUT request, we send the full resource representation, which includes the updated title as well as other attributes such as blog post body and so on that didn’t change. However, this approach would require that the client has the complete resource representation, which might not be possible if the resource is very big or has a lot of relationships. Additionally, this would require higher bandwidth for data transfers. So, for practical reasons, it is acceptable to design your API that tends to accept partial representations as part of a PUT request.
To support partial updates, a new method called PATCH has been added as part of RFC 5789 (http://www.ietf.org/rfc/rfc5789.txt). We will be looking at the PATCH method later in this chapter.
PUT is not a safe operation, as it changes the system state. However, it is considered idempotent, as putting the same resource once or more than once would produce the same result.
POST
Unlike PUT, a POST request doesn’t need to know the URI of the resource. The server is responsible for assigning an ID to the resource and deciding the URI where the resource is going to reside. In the previous example, the blogging application will process the POST request and create a new resource under http://blog.example.com/posts/12345, where “12345” is the server generated id. The Location header in the response contains the URL of the newly created resource.
The POST method is very flexible and is often used when no other HTTP method seems appropriate. Consider the scenario in which you would like to generate a thumbnail for a JPEG or PNG image. Here we ask the server to perform an action on the image binary data that we are submitting. HTTP methods such as GET and PUT don’t really fit here, as we are dealing with an RPC-style operation. Such scenarios are handled using the POST method.
The term “controller resource” has been used to describe executable resources that take inputs, perform some action, and return outputs. Although these types of resources don’t fit the true REST resource definition, they are very convenient to expose complex operations.
The POST method is not considered safe, as it changes system state. Also, multiple POST invocations would result in multiple resources being generated, making it nonidempotent.
PATCH
The request body contains a description of changes that need to be performed on the resource. In the example, the request body uses the "replace" command to indicate that the value of the "title" field needs to be replaced.
These correlations are true for Read and Delete operations. However, it is not as straightforward for Create/Update and POST/PUT. As you have seen earlier in this chapter, PUT can be used to create a resource as long as idempotency constraint is met. In the same way, it was never considered non-RESTful if POST is used for update (http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post). It is also possible for a client to use PATCH for updating a resource.
Therefore, it is important for API designers to use the right verbs for a given operation than simply using a 1-1 mapping with CRUD.
HTTP Status Codes
Informational codes—Status codes indicating that the server has received the request but hasn’t completed processing it. These intermediate response codes are in the 100 series.
Success codes—Status codes indicating that the request has been successfully received and processed. These codes are in the 200 series.
Redirection codes—Status codes indicating that the request has been processed but the client must perform an additional action to complete the request. These actions typically involve redirecting to a different location to get the resource. These codes are in the 300 series.
Client error codes—Status codes indicating that there was an error or a problem with client’s request. These codes are in the 400 series.
Server error codes—Status codes indicating that there was an error on the server while processing the client’s request. These codes are in the 500 series.
HTTP status codes and their descriptions
Status code | Description |
---|---|
100 (Continue) | Indicates that the server has received the first part of the request and the rest of the request should be sent. |
200 (OK) | Indicates that all went well with the request. |
201 (Created) | Indicates that request was completed and a new resource got created. |
202 (Accepted) | Indicates that request has been accepted but is still being processed. |
204 (No Content) | Indicates that the server has completed the request and has no entity body to send to the client. |
301 (Moved Permanently) | Indicates that the requested resource has been moved to a new location and a new URI needs to be used to access the resource. |
400 (Bad Request) | Indicates that the request is malformed and the server is not able to understand the request. |
401 (Unauthorized) | Indicates that the client needs to authenticate before accessing the resource. If the request already contains client’s credentials, then a 401 indicates invalid credentials (e.g., bad password). |
403 (Forbidden) | Indicates that the server understood the request but is refusing to fulfill it. This could be because the resource is being accessed from a blacklisted IP address or outside the approved time window. |
404 (Not Found) | Indicates that the resource at the requested URI doesn’t exist. |
406 (Not Acceptable) | Indicates that the server is capable of processing the request; however, the generated response may not be acceptable to the client. This happens when the client becomes too picky with its accept headers. |
500 (Internal Server Error) | Indicates that there was an error on the server while processing the request and that the request can’t be completed. |
503 (Service Unavailable) | Indicates that the request can’t be completed, as the server is overloaded or going through scheduled maintenance. |
Richardson’s Maturity Model
RMM can be valuable in understanding the different styles of web service and their designs, benefits, and trade-offs.
Level Zero
This is the most rudimentary maturity level for a service. Services in this level use HTTP as the transport mechanism and perform remote procedure calls on a single URI. Typically, POST or GET HTTP methods are employed for service calls. SOAP- and XML-RPC-based web services fall under this level.
Level One
The next level adheres to the REST principles more closely and introduces multiple URIs, one per resource. Complex functionality of a large service endpoint is broken down into multiple resources. However, services in this layer use one HTTP verb, typically POST, to perform all of the operations.
Level Two
Services in this level leverage HTTP protocol and make the right use of HTTP verbs and status codes available in the protocol. Web services implementing CRUD operations are good examples of level two services.
Level Three
This is the most mature level for a service and is built around the notion of Hypermedia as the Engine of Application State, or HATEOAS. Services in this level allow discoverability by providing responses that contain links to other related resources and controls that tell the client what to do next.
Building a RESTful API
- 1.
Identify resources—Central to REST are resources. We start modeling different resources that are of interest to our consumers. Often, these resources can be the application’s domain or entities. However, a one-to-one mapping is not always required.
- 2.
Identify endpoints—The next step is to design URIs that map resources to endpoints. In Chapter 4, we will look at best practices for designing and naming endpoints.
- 3.
Identify actions—Identify the HTTP methods that can be used to perform operations on the resources.
- 4.
Identify responses—Identify the supported resource representation for the request and response along with the right status codes to be returned.
In the rest of the book, we will look at best practices for designing a RESTful API and implementing it using Spring technologies.
Summary
REST has become the de facto standard for building services today. In this chapter, we covered the fundamentals of REST and abstractions such as resources, representations, URIs, and HTTP methods that make up REST’s Uniform Interface. We also looked at RMM, which provides a classification of REST services.
In the next chapter, we will take a deep dive into Spring and its related technologies that simplify REST service development.