As stated earlier in the previous article of REST (Introduction to REST), the Representational State Transfer (REST) style is an abstraction of the architectural elements within a distributed hypermedia system. REST distinguishes three classes of architectural elements, they are:
- Data Elements
Figure 1: REST Architectural Elements
Connectors represent the activities involved in accessing resources and transferring representations. Roles provide an interface for components to implement. REST encapsulates different activities of accessing and transferring representations into different connector types. The table below summarizes the connector types:
|Client||Sending requests, receiving responses.||HTTP library|
|Server||Listening for requests, sending responses.||Web Server API|
|Cache||Can be located at the client or server connector to save cacheable responses, can also be shared between several clients||Browser cache|
|Resolver||Transforms resource identifiers into network addresses.||bind (DNS lookup library)|
|Tunnel||Relays requests, any component can switch from active behavior to a tunnel behavior.||SOCKS, SSL after HTTP CONNECT|
In REST, the various software that interacts with one another are called components. They are categorized by roles summarized in the table below:
|Origin Server||Uses a server connector to receive the request, and is the definitive source for representations of its resources. Each server provides a generic interface to its services as a resource hierarchy.||Apache httpd, Microsoft IIS|
|User Agent||Uses a client connector to initiate a request and becomes the ultimate recipient of the response.||Browser like Netscape Navigator etc|
|Gateway||Act as both, client and server in order to forward - with possible translation - requests and responses.||Squid, CGI, Reverse Proxy|
|Proxy||CERN Proxy, Netscape Proxy, Gauntlet|
The key aspect of REST is the state of the data elements, its components communicate by transferring representations of the current or desired state of data elements. REST identifies six data elements: a resource, resource identifier, resource metadata, representation, representation metadata, and control data, shown in the table below:
|Resource||Any information that can be named is a resource. A resource is a conceptual mapping to a set of entities not the entity itself. Such a mapping can change over time. In general, a RESTful resource is anything that is addressable over the Web.||Title of a movie from IMDb, A Flash movie from YouTube, Images from Flickr etc|
|Resource Identifier||Every resource must have a name that uniquely identifies it. Under HTTP these are called URIs. Uniform Resource Identifier (URI) in a RESTful system is a hyperlink to a resource. It is the only means for clients and servers to exchange representations of resources.The relationship between URIs and resources is many to one. A resource can have multiple URIs which provide different information about the location of a resource.||Standardized format of URI:scheme://host:port/path?queryString#fragmente.g:http://some.domain.com/orderinfo?id=123|
|Resource metadata||This describes the resource. A metadata provides additional information such as location information, alternate resource identifiers for different formats or entity tag information about the resource itself.||Source link, vary|
|Representation||It is something that is sent back and forth between clients and servers. So, we never send or receive resources, only their representations. A representation captures the current or intended state of a resource. A particular resource may have multiple representations||Sequence of bytes, HTML document, archive document, image document|
|Representation metadata||This describes the representation.||Headers (media-type)|
|Control data||This defines the purpose of a message between components, such as the action being requested.||If-Modified-Since, If-Match|
Addressable means anything that can be accessed and transferred between clients and servers
Details of standardized format of a URI are as follows:
- scheme - It is the protocol you are using to communicate with. For RESTful web services, it is usually http or https.
- host - It is a DNS name or IP address
- port - This is optional and which is numeric The host and port represent the location of your resource on the network.
- path - This expression is a set of text segments delimited by the “/” character. Think of the path expression as a directory list of a file on your machine.
- ? - This character separates the path from the queryString.
- queryString -This is a list of parameters represented as name/value pairs. Each pair is delimited with the “&” character. Here’s an example query string within a URI:
- fragment: It is delimited by a “#” character. The fragment is usually used to point to a certain place in the document you are querying.
REST constraints are design rules that are applied to establish the distinct characteristics of the REST architectural style. If you follow all constraints designed by the REST architectural style your systems is considered RESTful.
These constraints don't dictate what kind of technology to use; they only define how data is transferred between components and what benefits we get following the guidelines. Therefore, a RESTful system can be implemented in any networking architecture available.
- Uniform Interface
- Layered System
- Code On Demand (Optional)
Let us see what each of these constraints mean:
This is a major constraint and it simply requires the existence of a client component that sends requests and a server component that receives requests. After receiving a request the server may also issue a response to the client. This constraint is based on the principle of Separation of concerns.
Figure 2: Separation of concerns
Note: Concerns are the different aspects of software functionality. For instance, the "business logic" of software is a concern, and the interface through which a person uses this logic is another. The separation of concerns is keeping the code for each of these concerns separate. Changing the interface should not require changing the business logic code, and vice versa. Model-View-Controller (MVC) design pattern is an excellent example of separating these concerns for better software maintainability.
Applying separation of concerns: Client-Server as in the diagram above:
- Separates user interface concerns from data storage concerns.
- Improves portability of interface across multiple platforms.
- Improves scalability by simplifying server components.
- Allows the components to evolve independently.
The notion of statelessness is defined from the perspective of the server. The constraint says that the server should not remember the state of the application. As a consequence, the client should send all information necessary for execution along with each request, because the server cannot reuse information from previous requests as it didn’t memorize them. All info needed is in message.
Figure 3: Stateless
By applying statelessness constraint:
- Session state is kept entirely on the client.
- Visibility is improved since a monitoring system does not have to look beyond a single request.
- Reliability is improved due to easier recoverability from partial failures.
- Scalability is improved due to not having to allocate resources for storing state
- Server does not have to manage resource usage across requests.
Stateless constraint has following tradeoffs:
- Reduced Network Performance
- Reduced server control over application consistency
REST includes cache constraints so that the "second fetch" doesn't have to be made at all if the data is already sitting in your local cache (or can be a request that only uses partial network resources if the cache is an intermediary or server-side). If your data can be designed in such a way to take advantage of this, you can reduce total network traffic by orders of magnitude.
Figure 4: Cacheable
By adding optional non-shared caching:
- Data within a response to a request is implicitly or explicitly labeled as cacheable or non cacheable.
- If a response is cacheable, then a client cache is given the right to reuse that response data for later, equivalent requests.
- Improves efficiency, scalability and user perceived performance.
- Tradeoff: cacheable constraint reduces Reliability.
The uniform interface constraint at a high level means the interface for a component needs to be generic as possible. It simplifies and decouples the architecture, which enables each part of the architecture to evolve independently.
Figure 5: Uniform Interface
By applying uniform interface constraint:
- Overall system architecture is simplified and the visibility of interactions is improved.
- Implementations are decoupled from the services they provide and encourage independent evolvability.
- Trade off: Degrades efficiency since information is transferred in a standardized form rather than one which is specific to application's needs.
Further a uniform interface has four sub-constraints
- Identification of resources.
- Manipulation of resources through representations
- Self descriptive messages
- Hypermedia as the engine of application state (HATEOAS)
These will be discussed later in the chapter REST Key Concepts
There are many layers between the client and the server. These are called intermediaries and can be added at various points in the request-response path without changing the interfaces between components, where they do things to passing messages such as translation or improving performance with caching. Intermediaries include proxies and gateways.
Proxies are chosen by the client, while gateways are chosen by the origin server or are imposed by the network.
Note: Proxy is an intermediary selected by a client, to provide interfaces to services like data translation, performance enhancement, or security protection. Gateway is an intermediary imposed by the network or origin server to provide an interface encapsulation of other services, for data translation, performance enhancement, or security enforcement.
Figure 6: Layered System
By applying a layered system constraint:
- Similar to client-server constraint this constraint improves simplicity by separating concerns.
- Can be used to encapsulate legacy services or protect new services from legacy clients.
- Intermediaries can be used to improve system scalability by enabling load balancing
- Placing shared caches at boundaries of organizational domain can result in significant benefits. Can also enforce security policies e.g. firewall.
- Intermediaries can actively transform message content since messages are self descriptive and their semantics are visible to the intermediaries
- Trade off: Adds overhead and latency and reduce user perceived performance.
Code on Demand
Code on demand is an optional constraint. It allows a client to download and execute code from a server.
Figure 7: Code on demand
By applying Code on demand constraint:
- Simplifies clients, hence promotes the reduced coupling of features.
- Improves scalability by virtue of the server off-loading work onto the clients.
- Trade off: Reduces visibility generated by the code itself, which is hard for an intermediary to interpret.
This article tried to explain the architectural elements of REST and REST Constraints concisely. Hope you likes the article.