-
Notifications
You must be signed in to change notification settings - Fork 1
Elements of RESTful Architecture
In the last module, we spent a lot of time looking at the constraints that make up the definition of REST. In this module, we're going to pay special attention to one of those constraints, the uniform interface and we'll see how it provides the key architectural elements that we'll use in the process of designing our own RESTful systems. Like I mentioned, the primary focus of this module will be on the additional constraints that make up the uniform interface. You can see those covered in bullet points 2 through 5. However, we're going to spend a few minutes upfront talking about two important elements that are used throughout the discussion. In fact, you've probably already heard them mentioned in the last module. Those are connectors and components and they describe how various pieces of a distributed architecture are organized and relate to each other via the uniform interface.
In REST, the various software elements that interact with one another are called components. And they're organized by the role they perform in the overall system. Fielding defines four major components types in his dissertation. Most of us are already used to the first two. An origin server is the ultimate destination that listens for requests and returns responses, sometimes with representations. One important thing to note from the REST perspective about the origin server is that it owns the URL name space for all of its resources. We'll talk a lot more about that little bit later. Similarly, most of us have a pretty good concept of the user agent component type. In fact, we likely hear the term and immediately think of a web browser which is without a doubt, the most common example of a user agent. A user agent is responsible for initiating a request or a state transition for resources. The last two component types are common intermediaries in a modern web architecture, that is they sit somewhere in between a user agent and an origin server to provide various kinds of additional processing from cashing to load balancing. The key distinction between a proxy and a gateway is that a proxy represents multiple user agents to the network. And as such, the client determines whether or not it will use a proxy. A gateway on the other hand represents multiple origin servers to the network. On the web today, there can be multiple layers of these intermediaries between a user agent and an origin server. Now we've identified components as being the units that work together in a RESTful design. Connectors can be thought of as the set of possible interfaces that a component can implement to accomplish its work. They're categorized by the role that they perform for a given component. Like user agents and origin servers, client and server are the most common types of connectors. A client connector initiates a resource request or a state transfer. A server connector listens for a request and responds. A cash connector, not surprisingly, manages a store of representations that can be reused for a specified amount of time and can directly respond to request for those representations. The last two are interesting, or at least they were for me. A resolver translates a resource identifier into whatever the right address format is so that two components can make a connection. A great example that Fielding uses of a real world resolver is how DNS translates a URL host name into an IP address which is necessary to initiate an actual TCP connection. TNS is just a well known example though. A resolver can be anything that translates something into an address that can be used to initiate a connection. The nice thing about using resolvers is that they provide a level of indirection between components and thereby extend the lifetime of a reference to a component. This is important when you take into account forces such as the fact that the network topology is always changing. Using the previous example, DNS enables the underlying IP address of a component to change without breaking the reference that another component may have to it. Finally, a tunnel connector relays communication across a boundary. A good example of this on the modern web can be seen and how proxies deal with SSL. In the event of a connect request, a proxy will switch to a tunnel for further requests. This is because SSL doesn't allow proxies to participate for obvious security reasons. One key thing to point out in the relationship between components and connectors is that a component can implement multiple connectors. For example, an intermediary such as a proxy implements at least the client and sever connector and it may vary like implements all of the connectors listed here. Like I mentioned a few minutes ago, this module is focused on REST's uniform interface constraint. This is the constraint that Fielding believed best differentiated REST from other architectural styles. And the constraints that define the uniform interface are probably the most practical when it comes to designing RESTful systems. So, looking quickly through the uniform interface constraints, you can see that we'll need to understand about resources, representations, messages and then possibly the worst acronym in the history of acronyms, "Hypermedia as the Engine of Application State."
Let's start with resources. These are the philosophers of the RESTful elements, their concepts, not entities like rose on a database, not even specific values, they are concepts. In more academic language, a resource is what the author intends to identify rather than any specific value. Another description that resonates with me is to say that a resource maps a concept to a set of entities over time. So while a resource is not an entity, a resource is related to entities and the relationship between a resource and entity is many to many. For a while, I had kind of a hard time coming to grabs with what this practically meant for a design. I think this was largely because when I would give talks on REST, my focus was always in juxtaposing a resource with a representation, more on representations in a bit. And so, I would regularly use the example of well, me. The example was, as I've mentioned earlier, I'm an illustration of a person resource and I can have many different representations such as a photograph, a business card and so on. And while this is a fine way to illustrate the difference between a resource and a representation, it made it more difficult for me to truly understand the mapping aspect of a resource, and this is because my example always map a single resource to a single entity, me. I needed a better example. So, I extended it out to include my family. If you look at the graph here, you can see that I have a few resources listed out. As you can see, this represent concepts like youngest child, it's not entities. One thing to point out here is that it's perfectly valid for all of these concepts to exist even when they don't have any actual entities mapped to them. When we introduce the entity Grace (phonetic), my oldest daughter, you can see that we now have a mapping. In this case, the mapping is between all of the concepts on the left to a single entity Grace. At the time of her arrival, Grace is my only child, my youngest child and my oldest child. Additionally, she has her own conceptual identity. Now, when Sarah (phonetic) came along, notice how the map changed. My children resource now points at two different entities. Also notice that at this point in time, while the concept of youngest child hasn't changed, and this means that the resource hasn't changed, the mapping between the resource and the entity that it maps to has changed. It's changed from Grace to Sarah. And then just to drive the point home, notice how the map here changes when Abigail (phonetic) comes along. The children resource now maps to three different entities. The youngest child resource now maps to Abigail. In all of the changes, Grace has remained oldest child and each of the kids have their own conceptual identity. The key point here though is that a resource itself is a concept and it should be a pretty stable concept in a system. What can and should change over time is the mapping between resources and the entities which form the values for those resources. One way that I've seen a design grow overly complicated and frustrating for the designer, is when the designer models resources as entities. This yields a fine grained explosion of relatively unstable resources.
If a resource is a unique concept in a system, it seems only fitting that it should have a unique identifier. The resource identifier then is how a server makes a resource available for interaction. Now, I said in the previous slide that a resource should be a relatively stable concept. And that translates here to say that a resource identifier should not change very frequently, though the mappings to underlining entities may very well be changing constantly. Additionally, while the mappings between entities and resources may change, the server is responsible for ensuring that the mapping is consistent with the meaning of the resource. Since after all, a resource is a concept. To use the example again of my family, it would change the meaning of the children resource if I were to suddenly start mapping that resource to pet entities regardless of how I feel about my pets. So, as we talked about a minute ago, a possible resource map of my family might look something like this. If I were building a RESTful system on HTTP and wanted to create identifiers for all these resources, it might look something like this. Here, I'm using URLs to identify the resources. I've created a resource hierarchy here with children at the top and resources like youngest and oldest as well as resources to identify each child. Now, if you're a database type of person, you might question whether or not it was wise to use the first names as the resource identifier and not a surrogate key. While there is absolutely nothing wrong with using a surrogate key to identify a resource, I deliberately chose to use first names here to remind you not to think about resources from an entity perspective. Now, when you're building RESTful systems on top of HTTP, you'll be using URLs to identify your resources and there are a couple of tools for accessing them. Scott Allen recently released on Pluralsight a great course on HTTP fundamentals, where in it, he showed this first tool called Fiddler. Fiddler is a fantastic tool that among other things lets you do two things that are really helpful when building and testing RESTful systems. First, it lets you monitor the communication between your client and service. It does this because it is an example of a proxy component, so it can sit between your user agent and your server and it functions as an intermediary to simply display the messages passing through it. The second function of Fiddler is that it allows you to create HTTP messages from scratch and send them to the server and then analyze the response. So in this capacity, Fiddler acts as a user agent. Another tool that you can use to create and view HTTP messages is a command-line tool called cURL. This tool is probably more popular in the Unix World but it absolutely runs on Windows and again, it's a very light weight user agent component that you can use to initiate requests or state transfers to a server.
One thing you'll see is we walk through the various elements of a RESTful design is that there's a lot different types of metadata that are an explicit part of an inter-component communication. This goes back to ensuring that the system has a high degree of visibility as we discussed back in module 2 with the stateless constraint. The first type of metadata that we see is metadata about the resource itself. Now in HTTP, nearly all types of metadata end up being flattened into the same place in a message exchange and this is in the HTTP headers. So whether or not a header qualifies as a specific type of metadata is really a matter of looking at it and determining what it's actually describing. In this case, you can see that in the message exchange, I've bolded two headers, the location header and the ETag header. Both of these headers tie very directly to the resource itself. The location header here is a link and is the actual resource identifier. The Etag represents the state of the resource at a point in time and can be used in scenarios like checking to see whether on not an item can be served from a cache even after its cache lifetime has expired. It can also be used to implement an optimistic concurrency strategy. For example, update the resource only if the ETag value has not changed. This would mean that the state of the resource had not changed.
Now, if a resource is a concept, a representation is something concrete that, well, represents that concept at a point in time. According to Fielding's definition, a representation is really just any sequence of bytes and there can be multiple representations for any given resource. As we'll see in just a bit, a representation can be tied to a bunch of different pivots or dimensions, not just the format which is what we most often think off when we think about representations. Now, it's great to have this separation between resource and representation as well as to have the ability to have different representations for a resource but it bears asking the question, why do we need this capability? There are two answers. The first reason is found in the nature of a resource. If a resource is an abstraction and functionally a map to a set of entities that change over time then there is really not a good way to manipulate that resource directly. It doesn't really exist concretely as something that can be manipulated. Also, the need for potentially multiple representations ties back to the very beginning of this course when we talked about heterogenous interoperability. And in an environment like the web, a service needs to support lots of different types of clients. Each with a different set of capabilities and expectations, and even those change over time. Representations provide the ability for servers to smoothly navigate this consistently shifting landscape. Finally, the process of ensuring that a client gets the best possible representation is called content negotiation. There are different categories of content negotiation and within those categories, there are different strategies for design and implementation. However, the two categories are server driven and agent driven. In server driven content negotiation, the server makes a decision about the representation sent to the client based on information in the client request. Remember that we want to ensure that we keep to the stateless constraint and keep everything nice and visible. In agent driven content negotiation, the user agent and the server worked together to determine the best representation. Now, this could mean different things but it typically means that the server provides the user agent with a set of choices in the form of links and the user agent dereferences one of those links. In the same way that we talked about resource metadata, representation metadata is simply data that's a part of the message that describes the representation. Again, in HTTP, it shows up as a part of the standard HTTP headers. So you'll need to look at the individual header to get a feel for whether or not it's describing the representation. The goal of this metadata is to help the clients and servers know what to do with the otherwise opaque byte stream that they're handed. So let's take a look at representations, representation metadata and content negotiation in action. Consider the following HTTP request, I've bolded some of the different headers that qualify as representation metadata as they specify this clients preferences. As you can see, the client would like to work with the JSON format which it declares by adding an accept header with the value of the JSON media type application/json. Media types which are also called content types are the standard way that representation formats are communicated in the web architecture. Nearly all of the formats that are core to the web like JSON are registered with an organization called IANA which stands for the Internet Assigned Numbers Authority. Additionally, you're free to create your own media type definitions though there are some naming conventions that you should follow. Check out the IANA website for more details on those conventions. At any rate, here we've specified a preference for JSON, we've also provided some information around the types of encoding, languages and character sets that our client knows how to deal with. We can then send this request over to the server at which point will get the following response. Now, notice in this exchange that the server simply returned a JSON representation. My client knows this because of some representation metadata in the response, notably, the content type header. This is an example of server driver content negotiation. In the case of agent driven negotiation, the server might still return JSON if it knew how to generate it but it may also send me back a list of links that I could dereference to get my resource in a different format. In fact, let's see what would happen if I wanted a different representation for that same resource. In this case, let's say that I wanted a picture of the Grace resource. I can simply change the representation metadata on the request as you can see here with the accept header, and because my server is capable of generating a PNG representation, I get a binary stream back along with the representation metadata of the standard image PNG media type. And because my client knows how to process this media type, I can then display a very cute image to my user.
Now, in adhering to the stateless constraint and ensuring that we have visibility in inter-component exchanges, we've talked about metadata for both resources and representations. This helps clients and servers know how to properly identify, locate and work with resources. However, what about all of that other stuff like letting a server know what to actually do with the representation, or letting the client know whether a request succeeded or failed. Remember that to be stateless, all of this information also needs to be a part of the uniform interface. Or, to put in other way, the messages should be self describing. Control data is the term that Fielding uses in describing this kind of information which is a part of the message exchange between clients and servers. It can be used to describe an intended action, the meaning of a response or override the default behavior of a connector. Let's jump back over to HTTP and take a look. Similar to the different types of metadata, control data exist for both request and responses in HTTP. For example, consider the following HTTP request, two pieces of control data to point out here are the get method which tells the server how to process the request and the If-None-Match header. This turns this get request into what's commonly called a conditional get request. As I mentioned in the previous slide, one of the things that control data enables you to do is override the default behavior of a connector, and this is true in the case of the If-None-Match header. It overrides the default behavior for handling get request and enables the server to produce a header only response if the value of the state of the resource has not changed. Then, let's look at the control data on the HTTP response. The most obvious data element is the status code. In our case, we return it to 100 status code indicating success. However, there are many other status codes that provide a rich set of semantics that we can use in designing components. In addition to the status code, we can see another great example of control data in the cache control header. This header provides instructions to a cache connector either on a user agent or any intermediary component with information on how long the response can remain stored in that cache.
The last constraint of the uniform interface is hypermedia. In my opinion, this is one of the most important constraints of the REST uniform interface because it's the one that most differentiates a RESTful design from and RPC design. Without hypermedia, it's still pretty easy to slip into an RPC design or a service structures it's URLs into resource hierarchies, makes proper use of metadata and control data and even enables multiple representation formats and content negotiation. If you remember from module 1, this kind of service if described by Richardson's maturity model as a level 2 service. This kind of service is not necessarily a bad design. In fact, this kind of design is likely representative of the overwhelming majority of currently deployed services. It's just not a RESTful design, as hypermedia is a constraint of the uniform interface. So what's the big deal about hypermedia then? Fundamentally, hypermedia is about dramatically reducing the coupling between a client and server. And by coupling here, I'm talking about a more implicit type of coupling. Specifically, hypermedia decouples a client from having to know the URLs of all the different resources that a service exposes. Instead, a hypermedia client should only need to know about a single entry point URL and should then be able to dereference links to interact with the other resources in the system. For example, let's revisit the online new site that we talked about back in module 1. It's highly unlikely that you find an article on a news website because you have foreknowledge of every page contained within that site. Instead, you navigate to the website's home page and you follow a link to an article that's relevant or interesting to you. Or, perhaps, you enter some keywords in a search box and click Submit. Both are valid examples of a hypermedia driven workflow. So hopefully, you can see that hypermedia is important to protect the client from having to know about a potentially prohibitive number of resource URLs. But it's also important to protect the server from getting locked in to a specific URL structure because a client or a few million clients have taken a dependency on a fixed structure at some point in time. As we said in module 2, one of the forces that REST was design to work with is the fact that the network topology is always changing. As such, the server needs to unilaterally own its own URL name space. Without hypermedia to drive clients through workflow, this name space ends up being jointly owned between the server and potentially lots of different types of clients making the evolution of the server difficult to impossible. I mentioned earlier that hypermedia is probably the most overlooked constraint in REST, and I think the reason for this is that people have too narrow of a definition for what it is. And as such have a difficult time seeing how it could possibly be sufficient for guiding a client through all of the various and possibly complex workflows in there system. And if you consider this first example, it's not difficult to see how this opinion could be formed. This link is one of the simplest and most common examples of a hypermedia control in the HTML media type. Now, I mentioned the media type deliberately here because I want to drive home the point that some media types like HTML already include hypermedia related language elements. These are called hypermedia controls. Other media types like XML and JSON which are arguably the most frequently used when building services are structural in nature and they don't by themselves include any hypermedia controls. This means that as a service author, it's your responsibility to either choose a media type that already has hypermedia controls or design a format which can include hypermedia controls. Now, back to the anchor tag displayed here, there are really two elements to mention. The first and most important is the rel attribute. This is used to identify the link relationship and it's what gives meaning to the link. To put it another way in the interest of making things a little more concrete, a client should not need to understand anything about the URL value of that href attribute. Instead, the client should be written to know only about the possible link relationship values and then on activating a control, simply follow the associated URL. We'll see much more about this when we talk about building RESTful clients in module 5. Now, this kind of hypermedia control is only good for get request. But what if I want to update a resource? The HTML media type provides additional hypermedia controls for creating and transferring representations. Here, you can see using the form in input elements. For me, this was the moment of realization where I started seeing hypermedia in a more complete context. In this example, I'm using the HTML class attribute as the place to declare my link relationship and this is because the form element doesn't have a rel attribute. The action attribute specifies the resource URL to address when the control is activated. And I'm also declaring the HTTP method to use for accessing the resource. I didn't have input elements to allow the client to fill in the values. And it should be pointed out here that the client doesn't have to be a human client. And at the end of all that, I transfer the state to the resource specified in the option attribute. Now, also keep in my mind that forms, even in HTML don't have to use post when submitted. If I were to specify "get" as my method here and test it in a browser, the name value pairs for my input elements would be transferred to the server as querystring parameters. Finally, while I've been talking about hypermedia as it relates to a representation format, there are media types that are not text based and would therefore have a problem conforming to these examples. In such cases, it's also possible to include hypermedia controls as a part of the representation metadata that we talked about earlier. For more information about this in HTTP, look for the HTTP link header proposal that is part of RFC 5988.
So as you saw in the previous example, hypermedia is much broader than simple links that fetch representations. The table here reflects a categorization scheme written about by Mike Amudsen in his book, Building Hypermedia APIs with HTML5 and Node. Because HTML is arguably the most hypermedia aware type that we have today, not to mention, it's the one that nearly everyone is familiar with. The examples for the different types here are shown in HTML. As you can see, links can be used to embed one representation within another as in the case of an image element. Naturally, links can also be used for getting representations as in the case of the HTML A element or anchor element as well as a form element within an action of "get" as we talked about just a second ago. The last two links are I think more interesting. An idempotent link is one that will yield the same effect each time it's activated and can therefore be activated over and over again. This is commonly associated with an HTTP put or delete method on the web. Conversely, a non-idempotent link is one that may have different effects each time it's activated. On the web, non-idempotent operations are most commonly associated with HTTP post. The link types in hypermedia controls that you'll select will very based on the details of your particular system. However, the basic workflow in a hypermedia driven system should generally look similar to the workflow shown here. All clients begin at a single entry point resource which returns a representation containing hypermedia controls. The client probably together with the user will select a hypermedia control based on the controls link relationship type. Remember, the link relationship is what gives meaning to the control not the URL itself. The client will then activate the hypermedia control, dereferencing the URL behind the control and initiating whatever state transition is described by it. In this case, the transition is an HTTP request to create a new child. As you can see, the control was selected based on the new child relationship type and it's likely that the hypermedia control directed the client to use HTTP post for the state transfer. Also, note that in additional hypermedia control was returned in the resource metadata of the response, in this case, using the HTTP location header.
To recap, the focus of this module was to identify the actual elements that make up the uniform interface and show how they relate back to the RESTful constraints identified in module 2, as well as help address the reasons for REST called out in module 1. With the conclusion of this module, you should now have all of the background and then some that you need to design RESTful systems. In the next couple of modules, we'll switch our attention from background and definitions to some practical tips for designing RESTful systems both services and clients.