Understand the REST

Before we get to the real concept, let’s be clear: REST, at its core, is a resource-oriented abstraction (as opposed to RPC, which is a process-oriented abstraction), and it is a guide to a design style rather than a strongly constrained protocol.

REST originated from Roy Thomas Fielding’s doctoral thesis “Architectural Stylesand the Design of network-based Software” published in 2000 Architectures “[1] presents a programming idea that gives the style of programming a name that many find difficult to understand but that is more common today — Representational State Transfer.

If we break it down into separate words Representational, State and Transfer, we know that they mean Representational, State and Transfer, respectively. But together, it seems that they do not understand what it is trying to express. By first understanding what HTTP is (after all, REST is built on TOP of HTTP), you can see that REST is actually a further abstraction of “HTT” (Hypertext Transfer), and the relationship between the two is the same as the relationship between the interface and the implementation class. REST is an abstraction of a resource. What is a resource?

Resource: For example, if you are reading an article called REST Design, the content of the article itself (which you can think of as information, data) is called a Resource. Whether you’re reading a book you bought, a web page on your browser, or a printed document, whether you’re reading it on a computer screen or a mobile phone, the information is the same. You’re still reading the same “resource.”

Then we use the passage as a resource to look at the meanings of representations, states, and transitions in the course of reading the passage:

Representation: When you read this article in a browser, the browser sends a request to the server that says, “I need the HTML for this resource.” The HTML that the server sends back to the browser is called a “representation.” You can also get PDF, Markdown, RSS, and other versions of this article in other ways. They are also multiple representations of a resource. Visible “representation” refers to the representation of information when it interacts with the user, which is in fact consistent with the semantics of the “PresentationLayer” often referred to in our software layered architecture.

State: When you’ve finished reading this article and want to see what’s next, you send a “send me the next article” request to the server. However, “next” is a relative concept and you must rely on “which one you are currently reading” in order to respond correctly. The contextual information that can be generated in certain contexts is called “status.” When the server was Stateful or Stateless, it was only relative to the server. The server had to either remember the state of the user itself, such as which article the user was reading. Either the client remembers the state, explicitly telling the server at the time of the request, such as I was reading such and such an article and now I want to read its next article, this is called stateless.

Transfer: Regardless of whether the state is provided by the server or the client, the “next article” behavior logic can only be provided by the server, because only the server owns the resource and its representation. The server somehow switches from “the article the user is currently reading” to “the next article.” This is called a “representational state transition.”

Six design principles for RESTful systems

1. Client-server separation

Separating the user interface logic from the data storage logic helps to improve the cross-platform portability of the user interface. Complete control based on the service side than in the past and rendering, such as JSP such model has little, on the one hand, ease of code warehouse and manageability become the obstacles of agile development, on the other hand benefit from the front-end technology (from ES specification to language implementation to the front-end framework, etc.) in recent years, the rapid development of contributed to today’s * * * front end separation mode: The back end controls the data and the front end controls the rendering.

They are Stateless.

REST expects that the server is not responsible for maintaining the state. Each request sent from the client should include all necessary context information. The session information is also maintained by the client, and the server only performs business processing logic based on the state passed by the client, driving the state changes of the entire application.

But the reality is that the amount of context state in a large system can swell to the point where the client can’t handle it, and holding some state in memory, session, database, or cache on the server becomes a de facto, long-lived, widely used mainstream solution.

3. Cacheability

Stateless services improve the visibility, reliability, and scalability of the system, but reduce the network of the system. The popular interpretation of “downnetworked” is that a feature may require one (or few) requests with a stateful design, but may require multiple requests with a stateless design, or contain additional redundant information in the request. To alleviate this contradiction, REST wants software systems that, like the World Wide Web, allow clients and intermediate communicators (such as proxies) to cache portions of the server response. Of course, in order for caching to work properly, the server response must indicate, either directly or indirectly, whether it can be cached and for how long, to prevent the client from getting stale data when it requests it in the future. A well-functioning caching mechanism can reduce the interaction between the client and the server, or even avoid interaction altogether in some scenarios, further improving performance.

4. Layered System

The layering here is not in the sense of representation layer, service layer and persistence layer, but rather that the client generally does not need to know whether it is directly connected to the final server or whether it is connected to an intermediate server on the path. Intermediate servers can improve the scalability of the system through load balancing and cache sharing, which facilitates the deployment of caching, scaling, and security policies. A typical application of this principle is a ContentDistribution Network (CDN). If you are reading this article through the website, the request you are making (assuming you are in China) is not directly to the source server at GitHub Pages, but to a CDN server in China, but as a user you do not need to be aware of this.

5. Uniform Interface

This is another core principle of REST. REST wants developers to program for resources, and it wants software system design to focus on abstracting what resources the system should have, rather than what behaviors (services) the system should have. You can understand this principle by analogy with the operation of file management on a computer. The operation of creating, modifying, deleting, moving, etc., can be countable, and the operation is fixed and uniform for all files. If the system is designed for resources, it will have similar operational characteristics. Since REST does not design a new protocol, these operations are performed by borrowing the operational commands inherent in the HTTP protocol.

The unified interface is also where REST is most vulnerable to debate, and the question of whether a web-based software system is more resource-oriented or service-oriented will probably remain open for a long time, if ever. However, there is a basically clear conclusion that resource-oriented programming is usually more abstract. The downside of being more abstract is that you tend to be farther away from human thinking, and the upside is that you tend to be more universal. For example, for the login and logout functions that almost every system has, it would be “human thinking” if you understood that the login corresponds to the login() service and the logout corresponds to the logout() service. If you think of logging in as a PUT Session and logging out as a DELETE Session, then all you need to do is design a “Session resource” to meet that requirement, and even if you want to ask for information about the user who logged in, you can just GET a Session. Other actions, such as modifying user information, can also be included in the same design, which is the benefit of “higher abstraction”.

To properly utilize a unified interface in an architectural design, Fielding recommends that the system include the resource ID on each request, and that all operations are performed through the resource ID. It is suggested that each resource should be a self-describing message; Hypertext is recommended to drive application state transition

6. Code-on-demand

On demand code is listed by Fielding as an optional principle. It refers to any technology that sends executable software programs from a server to a client on request, such as a browser. On demand code gives the client the luxury of not having to know in advance how all the information from the server should be handled and how it should run. For example, JavaApplet technology in the past, WebAssembly today, etc., are typical on-demand code. The code containing the specific execution logic is stored on the server, and only after the client requests a JavaApplet is the code transferred and run on the client machine. It is usually immediately destroyed in the client. The reason for making code on Demand an optional principle is not so much that it is particularly difficult to achieve, but more a practical matter of necessity and cost effectiveness.

RMM (Richardson MaturityModel)

Leonard Richardson, author of RESTful Web APIs and RESTful Web Services, has come up with the Richardson MaturityModel, which measures how RESTful Services are. RMM) so that systems that do not use REST can import REST gradually. Richardson ranked service interfaces on a scale of “REST” from 0 to 3, from low to high.

  • Level 0 (The Swamp of Plain Old XML) : No REST at all.
  • Level 1 (Resources) : Start introducing the concept of Resources.
  • Level 2 (HTTP Verbs) : Introduce a unified interface that maps to methods of the HTTP protocol.
  • Level 3 (Hypermedia Controls) : Hypermedia control, in this article, is “Hypertext driven,” and in Fielding’s paper, “Hypertext As The Engine Of ApplicationState, HATEOAS,” which all mean The same thing.

Using an example from Martin Fowler’s article on RMM (written in XML, simplified here to JSON), let’s show how the four different degrees of REST are reflected in the actual interface. If you were the programmer, what would you do?

Doctor appointment System

As a patient, I want to know from the system whether a doctor I know has free time on a given date, so that I can make an appointment with that doctor. Design two RESTful interfaces: one for querying idle time and one for booking medical treatment.

0 level

The hospital opens a Web API of /appointmentService, and passes in parameters such as date and doctor name to get the free time of the doctor within the time range. An HTTP call of this API is shown as follows:

POST /appointmentService? query HTTP / 1.1

{"data": "2020-03-04"."doctor": "mjones"}
Copy the code

The server then sends back a response containing the required information:

HTTP / 1.1 200 OK

[{"start":"Then"."end":"Name"."doctor": "mjones"},
    {"start":"Our"."end":"Thus" were."doctor": "mjones"}]Copy the code

After getting the result that the doctor was available, the author thought that 14:00 was more appropriate, so he confirmed the appointment and submitted his basic personal information:

POST /appointmentService? action=confirm HTTP / 1.1

{
    "appointment": {"date": "2020-03-04"."start":"Then"."end":"Name"."doctor": "mjones"},
    "patient": {"name": "zio"."age": 30. }}Copy the code

If the appointment is successful, THEN I can receive a successful response:

HTTP / 1.1 200 OK

{
    "code": 0."message": "Successful confirmation of appiontment"
}
Copy the code

If something goes wrong, such as someone booking ahead of me, I get some kind of error message in the response:

HTTP / 1.1 200 OK

{
    "code": 1."message": "doctor not available"
}
Copy the code

So far, the entire reservation service declaration is complete, and it is straightforward and straightforward. We adopt a very intuitive service design based on RPC style

Level 1

Level 0 is the style of RPC, and it works perfectly well if the requirements never change. However, if you don’t want to write additional methods for doing something other than making a doctor appointment, getting information other than free time, or modifying the interface to an existing method, you should consider how to abstract resources using REST.

The first step towards REST is to introduce the concept of resources, which is basically represented in the API by designing services around resources rather than processes. To put it more plainly, this means that the Endpoint of a service should be a noun rather than a verb. In addition, the resource ID should be included in each request, and all operations are performed through the resource ID, for example, to get the idle schedule at the time specified by the doctor:

GET /doctors/mjones? date="2020-03-04" HTTP / 1.1
Copy the code

The server then returns a list of schedules containing the ID information. Note that the ID is the unique number of the resource, and having an ID means “doctor schedules” is considered a resource:

HTTP / 1.1 200 OK

[{"id": 1."start":"Then"."end":"Name"."doctor": "mjones"},
    {"id": 2."start":"Our"."end":"Thus" were."doctor": "mjones"}]Copy the code

I still think the time of 14:00 is more appropriate, so I confirm the appointment again and submit my basic personal information:

POST /schedules/1 HTTP / 1.1

{"name": "zio"."age":30. }Copy the code

Subsequent reservation success or failure response messages are the same at this level and are not repeated. Level than 0, the characteristics of level 1 is the introduction of the resources, through resource ID as the main clue and service interaction, but level 1 there are at least three questions unsolved: one is to deal with only the query and make an appointment, if want to change the time temporarily, to adjust the booking, or disease good, suddenly want to delete the booking, this all needs to provide a new service interface; Second, when processing the result response, it can only rely on the code and message fields in the result to make branch judgment. Each set of services should design code that may occur errors, which is difficult to consider comprehensively and is not good for unified processing of some common errors. Third, security aspects such as authentication and authorization are not taken into account. For example, only loggedusers are allowed to query the doctor schedule, some doctors may only be open to VIPs, and patients of a certain level are required to make an appointment, etc.

The second level

All three of the remaining issues at Level 1 can be addressed by introducing a unified interface. The seven standard approaches to the HTTP protocol have been carefully designed to cover almost any operational scenario a resource might encounter, provided the architect’s abstraction capabilities are sufficient. The specific practices of REST are as follows: To solve the first problem, different business requirements are abstracted into operations such as adding, modifying, and deleting resources; The second problem can be solved by using HTTP protocol Status Code, which can cover most of the possible exceptions of resource operations, as well as custom extensions; Relying on HTTP headers to carry additional authentication and authorization information solves the third problem, which is not shown in practice.

According to this idea, the doctor schedule should be obtained by using GET operation with query semantics:

GET /doctors/mjones/schedule? date=2020-03-04&status=open HTTP / 1.1
Copy the code

The server then sends back a response containing the required information:

HTTP / 1.1 200 OK

[{"id": 1."start":"Then"."end":"Name"."doctor": "mjones"},
    {"id": 2."start":"Our"."end":"Thus" were."doctor": "mjones"}]Copy the code

I still think the time of 14:00 is more appropriate, so I confirm the appointment and submit my basic personal information to create the appointment, which is in line with the semantics of POST:

POST /schedules/1 HTTP / 1.1

{"name": "zio"."age":30. }Copy the code

If the appointment is successful, the author will receive a successful response:

HTTP / 1.1 201 Created

Successful confirmation of appointment
Copy the code

[inset] If something goes wrong, say someone preempted the reservation, the author will receive some kind of error message in the response:

HTTP / 1.1 409 Conflict

doctor not available
Copy the code

Level 3

Level 2 is the REST level reached by most systems today, but it is still not perfect, at least one question remains: how do you know that the schedule to schedule Dr. Mjones requires access to the service Endpoint “/schedules/1234”? Maybe you can’t even understand why I have such a question at the first time. Of course, this is written in program code! But REST doesn’t buy into this idea, which has long been baked into the minds of programmers. The hope for hypertext control in RMM, HATEOAS in Fielding’s paper, and more “hypertext-driven” now is that, with the exception of the first request being driven by your input in the browser’s address bar, other requests should be able to describe the state transitions that may occur, driven by the hypertext itself. So, after you enter the query command:

GET /doctors/mjones/schedule? date=2020-03-04&status=open HTTP / 1.1
Copy the code

The response from the server should include possible follow-up actions such as how to schedule appointments, how to learn about doctors, and so on:

HTTP / 1.1 200 OK

[{"id": 1."start":"Then"."end":"Name"."doctor": "mjones"."links": [{"rel": "confirm schedule"."href": "/schedule/1"}]}, {"id": 2."start":"Our"."end":"Thus" were."doctor": "mjones"."links": [{"rel": "confirm schedule"."href": "/schedule/2"}}]]Copy the code

With level 3 REST, the server API is completely decoupled from the client, making it much easier to adjust the number of services or upgrade the API for the same service.

For level 3, be clear: If the client is referring to a scenario where the release and upgrade costs are high on mobile, then this is a very friendly design. However, if the client is the Web front end, their publishing cost is the same as the server side, then we can look at the case by case, whether to maintain this kind of “href” information on the server side, whether it is against the “front and back end separation” idea.

Recommended reading

Phoenix Architecture · Building reliable Large-scale Distributed Systems