RESTful API Design Getting Started Guide

Background and overview

In the iteration of technology, the front end and rear end separated development has become the dominant mode of modern development. The core of connecting the back end and front end is a series of APIs provided by the back end to the front end for interaction.

In actual development, a set of interfaces provided by back-end services may be used for different front-end applications such as the Web, mobile APP, desktop, and even for other programs (for example, different modules of microservices call each other).

If the interface provided by the back-end service can have Easy to understand, clear standards, and extension friendly Its features will be conducive to subsequent development and reduce costs. and Interface designed according to REST style It just has these characteristics, so it becomes popular in modern Web development.

Although there are a lot of REST materials on the Internet, few are comprehensive, accurate, easy to read, and relevant to practice, which is of limited help to some problems encountered in actual development. So in the process of learning and development, I reorganized and wrote this article, including definition and explanation, comparison with traditional APIs, actual development problems, etc., and summarized it as comprehensively as possible from the perspective of developers, hoping to help readers better understand and use REST.

Note: If you already know about REST, you can go directly from In depth RESTful API This chapter begins to read. The article comes with some demo codes written in Java's SpringMVC/Boot and JS's Axios, which can be selectively read or skipped as needed.

What are REST and RESTful APIs

Start from definition for preciseness: full name of REST Representational State Transfer , literally translated as: Expressive state transition , provides a set of design principles. (Refer to Baidu Encyclopedia)

This name is confusing because REST omits a subject: "resource". Wherein, "representational" refers to the performance of resources.

In that case, the core of REST is: resources performance state transition These three parts. Let's understand it in a more popular way.

  • resources (Resource): The content that the client wants to request. Pages, pictures, files on the server, and even data processed by the server (such as data queried from the database) are resources. In REST, the concept of resource includes two parts: one is the resource itself, and the other is an identifier that can describe such resources.
    The description of the original paper is that resources are conceptual mappings to a group of entities, not mapped entities corresponding to any specific point in time. (A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.)
  • performance Representation ): refers to how resources are displayed. A resource can have multiple "representations". For example, an interface can return data queried from a database, either in JSON or XML, which is the same resource's Different performance It generally includes two parts: data and metadata Corresponding to the HTTP response, it is the field describing data in the response body and header (for example Content-Type )。
  • state transition : It may be difficult to find a precise definition of this. But it can be basically understood as: when the content of a resource changes, it is transferred from one state to another. In the case of the client server model of the network, the client sends different requests to push the resources on the server to change.

One understanding of REST simplification is that resources are displayed in a certain way and can be driven to change by the client.

Referring to IBM's documentation, REST proposes six Design principles (also known as schema constraints):

  1. Unified interface. No matter where the request comes from, all API requests made to the same resource should look the same. The API should ensure that the same piece of data (such as a user's name or e-mail address) belongs to only one Uniform Resource Identifier (URI). Resources should not be too large, but should include every piece of information that customers may need.
  2. No status. The API is stateless, which means that every request needs to contain all the information needed to process it. In other words, the API does not require any server-side sessions. The server application is not allowed to store any data related to the client request.
  3. Cacheability. If possible, resources should be cacheable on the client or server side. The server response also needs to contain information about whether caching of delivered resources is allowed. The goal is to improve the performance of the client and enhance the scalability of the server.
  4. Client/server decoupling. In API design, client and server applications must be completely independent of each other. The client application only needs to know the URI of the requested resource; It cannot interact with server applications in any other way. Similarly, the server application should not modify the client application other than passing it to the requested data via HTTP.
  5. Layered system architecture. In the API, calls and responses go through multiple different layers. Similar to the current microservice architecture, the request will go through the forwarding (proxy) server, API gateway and other levels before entering the real server program.
  6. Encoding on demand (optional). The API usually sends static resources, but in some cases, the response can also contain executable code (such as Java applets). In these cases, the code should only run on demand.

 

according to REST style The designed API is called RESTful API (Also called REST API directly in some places). The first three principles should be the main focus of API design. 4 and 5 have been met in the current software architecture.

Transition from traditional API to RESTful API

So far, we have all talked about REST at the definition and theoretical level. Now let's ignore the details of the implementation and look at an example of traditional API design, so that we can link REST with API design.

Suppose we want to develop a Library management system , one of which is aimed at Book data Do "Add, Delete, Modify, Query". After the design is handed over to different people, the following interfaces may be obtained (ignore the request and response data first):

 Book management API developed by Party A Query the specified book GET/which? id=1 Query multiple books GET/all New book POST/create book Modify book POST/update book Delete book POST/del book
 Book management API developed by B Query the specified book POST/MyBook? id=1 Query multiple books POST/AllBook New Book PUT/book/addBook PUT/book/modify Delete book POST/removeBook

Look, for example, for the same modified interface, A and B use different request methods and path structures. This exposes a defect in the traditional API design: there is no standard or rule for URLs and usage methods (HTTP request methods) of different interfaces.

Single person development projects may not matter. However, there is no unified design specification for API in large-scale systems and multi person cooperation projects The interface is difficult to understand and use , or even increase the difficulty of maintenance.

Another problem is that we have no clear specification for the HTTP request mode supported by the interface. For example, we can choose to design a DELETE /getMyUser/id=1 And then return the response data in binary form. This is certainly a feasible solution, but it is somewhat strange.

If it is in a team, we can try to agree on some "hard specifications" in order to better develop. For example:

  • URL structures are Verb abbreviation+noun , multi-level is not allowed
  • Force query to get Start with, New with add Start with, delete with del start
  • GET request for query and POST for others

We can design similar APIs for the "Add, Delete, Modify, Query" interfaces of different functions. For example:

 Query books GET/getAllBook New Books POST/addBooks Delete Books POST/delBooks
 Query user GET/getUser New user POST/addUser Delete user POST/delUser

It looks better than before. It can be formulated as a specification (or style) of interface design.

But can it be further refined? For example, can you continue to refine the path hierarchy of the URL? Even unified singular and plural?

Of course. with Addition, deletion, modification and query of books Interface as an example, assume that the identifier (i.e. URI) is /books , one way is:

 Query books POST/books/get New Book POST/books/add Delete Books POST/books/del

Or, simply omit actions in the URL and directly use HTTP request methods to express different operations on resources. For example:

 Query books GET/books New Book POST/books Delete Books

The last two design ideas have begun to move towards RESTful APIs.

In depth RESTful API

If you try to resources From the point of view of adding, deleting, modifying and querying APIs, we can get a way of understanding: the goals of these interface operations are resources , different interfaces pair resources Different operations were performed

For example, in the above example, the resources for interface operations are book , different interfaces only book Different operations were performed. (In other words, the book resource has undergone a state transition)

The core of RESTful API is to use HTTP request method As an operation type (behavior), you can use the identifier (URI) Determine the resources to be operated (i.e. the unified interface in REST principles), and at the same time meet the design principles of REST.

HTTP request method as behavior (core content)

All the request methods defined in HTTP can be found on this page: HTTP request method

In RESTful API, different HTTP request methods express clear meanings:

method Function description Idempotent or not Corresponding back-end operations
GET Returns the value of an object True query
POST Create a new object, or submit a command False increase
DELETE Delete an object True delete
PUT Update an object (full replacement), or create an object with an identifier True Full update
PATCH Update an object (partial update) False Partial update
OPTIONS Get the information of a request, that is, get the request method supported by a URL True nothing
HEAD Returns the metadata in an object GET response, which is equivalent to a GET without a response body True Query/None

among OPTIONS and HEAD Relatively few are used. Other HTTP request methods rarely appear in RESTful APIs, so we will not discuss them.

An interface does not need to support all methods. But for supported methods, interfaces Must conform to the corresponding usage

In addition, if conditions permit, it is suggested to add OPTIONS To facilitate the client to retrieve the information of this interface (instead of having to query the API document). Its response header should at least contain Allow , recommended plus Link The URL of the documentation that points to this interface. For example:

 Allow: PATCH, DELETE Link: < https://example.com/doc >;  rel="help"

URL Basic Structure

The URL of the RESTful API should be Easy to read and spell Of. Referring to different tutorials and Github APIs, the structure of common URLs can be summarized as follows (brackets indicate optional):

 Http (s)://Web address or IP (: port) (/subpath) (/version number)/resource name (/path parameter) (? Query parameter name=parameter value)

For example:

 A well structured URL: https://api.contoso.com/v1.0/people/jdoe @contoso.com/inbox https://api.contoso.com/v1.0/items?url=https://resources.contoso.com/shoes/fancy Unfriendly URL (not recommended): https://api.contoso.com/EWS/OData/Users (' jdoe@microsoft.com ')/Folders('AAMkADdiYzI1MjUzLTk4MjQtNDQ1Yy05YjJkLWNlMzMzYmIzNTY0MwAuAAAAAACzMsPHYH6HQoSwfdpDx-2bAQCXhUk6PC1dS7AERFluCgBfAAABo58UAAA=')

commonly Path parameter It is used to specify the ID, name and other unique parameters in similar resources, Query parameters It is used to transfer filter conditions.

Extension: query parameters and path parameters

Query parameters are also called filter parameters. As the name implies, they are generally used to pass parameters to the backend and filter some data. Write at the end of the URL to ? Start with multiple parameters & connect. Common in GET and DELETE.

The path parameter is written in the URL path / separate.

Respectively correspond in SpringMVC @RequestParam And @PathVariable

Yes for Hierarchy Data, where Resource name and Path parameter Multiple can appear (the path parameter follows the resource name).

 Github's API also has multiple levels. "OWNER" and "REPO" are the owner name and warehouse name respectively: https://api.github.com/repos/OWNER/REPO/interaction-limits https://api.github.com/repos/OWNER/REPO/actions/workflows/WORKFLOW_ID/disable For example, the first item indicates the "interaction restriction" under the "REPO" warehouse owned by the "OWNER" user in the "Repos" resource

In addition, there are some piecemeal conventions:

  • URI No The verb (action) appears.
    Because the RESTful API has used HTTP methods to represent the operations performed on resources, it is easy to cause ambiguity if the operations occur again in the URI. For example: GET /books/1 Is appropriate, GET /book/get/1 Is inappropriate
  • Resource name should Use the plural form of nouns.
    Resource is a kind of resource, and the plural number expresses the meaning of the same kind of resource set. It is also OK to use singular numbers, but remember to keep the global consistency, and do not mix singular and plural numbers.
  • Letters in URL should It is in lower case. Use hyphens for phrases - Segmentation, not hump nomenclature.
    To be exact, the URI is lowercase. However, if possible, other parts of the URL (such as path parameters) should also be kept in lower case.

In addition, whether the URI is available abbreviation There seems to be no clear consensus. For readability, proposal Use fewer abbreviations and only common abbreviations, such as: CPU,PC

Several common misunderstandings about RESTful API:

  • Using JSON is RESTful API
  • URL query parameter cannot appear in URL( ? Mode parameter transmission)

Note: The examples in this article are used for front end and back end interaction, but the application scenarios of RESTful API are not limited to this. REST can be considered as long as the API is developed.

Simple Spring MVC code example

For the convenience of back-end developers who are not familiar with HTTP, here is also a simplified Spring MVC code example.
The client requested these addresses:

 GET  https://api.gameinfo.com/developers/mihoyo/games/genshin-impact GET  https://api.gameinfo.com/games/1044620

Corresponding Controller definition:

 @RestController public class GameProductController { @GetMapping("/developers/{developerName}/games/{gameName}") public Object getGameById(@PathVariable String developerName, @PathVariable String gameName) { //Query the gameName game developed by the manufacturer named developerName } @GetMapping("/games/{gid}") public Object getGameById(@PathVariable Integer gid) { //Query games with ID of gid } }

URL and URI (Extended)

To facilitate understanding, here is a brief explanation of the difference between URL and URI. Also see this article: The difference and relationship between uri and url

URL I.e Uniform Resource Locator : called Uniform Resource Locator , indicating the address of an exact network resource.

URI I.e Uniform Resource Identifier : called Uniform Resource Identifier , used to identify Web Any type of resources (HTML, video, audio, programs) that can be accessed on the. URI is relative, and its main function is to distinguish it from other resources.

From a level perspective, a URL is a subset of URIs, and URLs are more detailed than URIs. For example, for the server, https://api.example.com/books/1003?param=abc Is the URL where /books/1003 Is a URI.

REST design criteria (from REST to RESTful API)

Now we have a preliminary understanding of REST and RESTful APIs. Let's pause for a moment and think about a question: What do we want to talk about REST when designing APIs? One word: design criteria (Designing Guideline)。

For us, using REST to design is more meaningful than just understanding REST. This requires a further refinement of the criteria (equivalent to project development specifications in some respects).

Please remember REST is a style of design , not a Clear implementation standards

reference resources Microsoft REST API Design Guidelines A book (hereinafter referred to as the Design Guide), the criteria have at least the following two functions:

  • Refine implementation schemes and constraints
  • Keep the designed API consistent

Borrow here Design Guidelines In a sentence in, the guidelines are established to develop interfaces consistently.

This document establishes the guidelines Microsoft REST APIs SHOULD follow so RESTful interfaces are developed consistently.

Before the formal start of RESTful API design and development, it is recommended to refine a set of design guidelines based on REST, which is more important for team development.

Difference from traditional API

  • Consistency basics
    REST style provides a relatively complete design standard for API design, which is consistent and easy for human to read and understand. (Recommended reading Design Guidelines Chapter 7)
  • Emphasize semantics
    The predicate verb (HTTP request method) corresponds to the action (add, delete, modify, query, etc.) executed one by one.
  • Idempotence
    Traditional APIs do not attach much importance to interface idempotence. The HTTP request method of RESTful API is also associated with idempotence This feature should also be guaranteed when implementing this interface
  • Versioning
    When designing RESTful APIs recommend Add in URL Version No (Note that the URL is not a URI). This is mandatory in some guidelines (e.g Design Guidelines Chapter 12). The format of the version number is {majorVersion}. {minorVersion} , for example:

     #Add at URL path: https://api.contoso.com/v1.0/products/users #As URL filter parameter https://api.contoso.com/products/users?api -version=1.0

    in addition Design Guidelines Services MUST increase their version number in response to any breaking API change (When any destructive API change occurs, the server should add the version number in the response.)

Advantages and disadvantages

Adherence to REST style can bring us the following benefits:

  • More unified interface URL style to improve the readability of API( Human readable
    This idea is equivalent to the development specifications of some companies that require a good name for variables and methods
  • Eliminate ambiguity caused by improper URL design and ensure idempotence
  • More elegant handling of new interfaces (interface definition changes)
    If you want to modify the API, one option is to update the version number of the API instead of doing it on the original basis Backward compatible. this It reduces the probability of defining URL conflicts, and also simplifies the naming of new and old interfaces

Possible benefits:

  • Stateless APIs are easier to develop than stateful APIs
  • Make full use of the characteristics of HTTP itself

But there are also disadvantages:

  • REST style is mainly aimed at about resources Processing of Instead of emphasizing action , increasing the difficulty of API design
  • Developers need to have a certain understanding of HTTP protocol, front-end and back-end frameworks (such as Spring MVC, Axios)
  • You may need a little English vocabulary when designing names

Request and Response

In the HTTP protocol, the request and response both contain the header and payload. RESTful APIs also use headers to transfer some data, which should be noted during design.

In the request, the common and important headers are:

Response Header type describe
Authorization character string Requested authorization data
Date date The requested timestamp is based on the client time and uses RFC 5322 date and time format. The server should not rely on the accuracy of the client time. This header is optional, but if there is one, the client must use Greenwich Mean Time (GMT), for example: Wed, 24 Aug 2016 18:41:30 GMT. Here GMT and UTC are equivalent.
Accept Content Type The response body data type expected by the client, such as application/xml, application/json, text/javascript (for JSONP). For the server, this header is a hint, and the server can also return different content types.
Accept-Encoding Gzip, deflate When appropriate, the REST endpoint (API) should support GZIP and DEFLATE encoding
Accept-Language "en", "es", etc. Specifies the language in which the client expects the response. The server does not have to support it, but it needs this field to provide localization support.
Accept-Charset Character set type, such as "UTF-8" The default is UTF-8, but the server should also be able to handle ISO-8859-1
Content-Type Content Type Mime type of the request body (for example: application/json, PUT/POST/PATCH)

 

The response should at least include the following:

Response Header describe
Content-Type The content type of the response body (for example: application/json)
Content-Encoding GZIP or DEFLATE, depending on the situation
Date The time stamp for processing the response, which is based on the server time and uses RFC 5322 date and time format. The time standard is Greenwich Mean Time (GMT), for example: Wed, 24 Aug 2016 18:41:30 GMT. Here GMT and UTC are equivalent.

Some heads will appear in specific scenes (for example Prefer If-Match Etc.), which is used to inform the customer/server of some information. It is only mentioned here but not expanded.

And remember, The HTTP response code is also semantic In other words, the server needs to select and return the appropriate HTTP response code according to the specific situation. The common ones are as follows:

  • 200: Success
  • 201: The resource has been created (usually the POST method is added successfully, and there is no response body)
  • 204: No content (generally the PUT method is successfully processed and there is no response body)
  • 400: Incorrect requests that the server cannot process
  • 404: Resource not found

Generally speaking, you can select directly according to the meaning of each status code specified in the HTTP protocol. More detailed examples can also be referred to Microsoft REST API Design Guide

In actual development, many people do not care about this section. Because in modern development, the technical framework can be used to automatically process the header, and the business layer does not need to handle it manually.

How to handle more actions?

The main body of RESTful API operations is resources, and the commonly used HTTP methods are very suitable for the requirements of "adding, deleting, modifying and querying". But for APIs that focus on expressing actions (operations) and operations other than "add, delete, modify, query", it seems difficult to design according to REST.

This requires us to further abstract the business. Two references are also given here.

One way is to Result of action As an abstract resource result Operate. Similar ideas can be found in GitHub's API, such as blocking and unblocking user interfaces( Block User - GitHub Docs ):

  • Block a user from an organization:PUT /orgs/{org}/blocks/{username}
  • Unblock a user from an organization:DELETE /orgs/{org}/blocks/{username}

At this time, the operating resource is not the user, but the Blocked user list As a resource. Blocking and unblocking users means putting and deleting the list contents.

Another way is to set receiver of an action Think of it as a resource and parameterize the operation or operation result.

Another example is to design a Start or stop executing the specified task Interface. here task It can be regarded as a resource to be operated; Because the task will be changed after starting and stopping the two actions running state , we can running state Designed as a parameter. It is used as follows:

 //Start Task PATCH /tasks/{id} Content-Type: application/json { "status": "start" } //Stop Task PATCH /tasks/{id} Content-Type: application/json { "status": "stop" }

Such APIs generally only need to respond successfully, so after processing, they can only respond 204 without the response body.

In addition, please note that HTTP request modes such as PUT and DELETE are likely to be selected in such cases, and the idempotence of the interface needs to be properly handled during development.

Problems and difficulties in practical application

By now, we are familiar with REST itself and its basic usage. However, once the application is started, there are new problems. In this chapter, we will look at some common problems and difficulties.

HTTPS usage and implementation scheme

Do you want HTTPS? No, but HTTPS is highly recommended because:

  1. More secure
  2. The SSL connection is established between the TCP layer and the HTTP layer. The URL (path and parameters) is always encrypted

According to different security requirements, there are different implementation schemes for enabling HTTPS:

  • If it is a microservice architecture, it can consider that the application (API) level does not do encryption, the upper gateway does HTTPS, and then the gateway exposes the API externally.
  • Enable encryption on the CDN. The server only allows access to the CDN node.

Selection of response body form (use JSON?)

First, let's conclude that the response format specified by the client is preferred( Accept Header), if the server can choose JSON first.

REST and Do not force resources to return (That is, the representation of resources is diverse). Theoretically, the interface forces the return of XML or other formats to conform to REST, but this design results in a bad API. A better design is to let the API return the specified type in the request header first (respond if not supported 415 Unsupported Media Type ), rather than returning JSON anyway.

To simplify development, you can refer to the following two implementation ideas (take SpringMVC as an example):

  1. Unified judgment at the framework level Accept And transform the response content (such as custom interceptors)
  2. The controller (business layer) only returns JSON and intercepts all Accept Field is not application/json 's request (lazy practice)

Use GET to transfer Body

In the HTTP protocol, both There is no explicit stipulation that GET cannot take the body , also Didn't say they could take the body However, different programs have different processing methods. For example, Chrome will erase the Body part by default when it initiates a GET request, but curl will not.

Curl request demo

Backend key code (Controller of SpringMVC):

 @RestController public class TestController { @GetMapping("test") public String queryUser(@RequestBody UserParam userParam) { System.out.println("userParam = " + userParam); return "{\\"user\\":\\"Steve\\",\\"mail\\":\\" ste118@exmail.com \\"}"; } } record UserParam(String name, Integer age) {}

Initiate the request in GET mode through curl, and join the request body at the same time

The backend (Spring MVC) can correctly extract the data in the request body and pass it into the method parameters.

So considering the compatibility issue, Not recommended Take the body with you during GET. If you need to pass parameters, use Filter parameters (URL parameters), The same goes for DELETE. It is also recommended that the client escape the special characters in the parameters (do URL Encode once).

Spring MVC code example

For example, when the front-end requests an API, use GET /test?name=steve&age=2 (No HTTP Body) The backend uses SpringBoot as an example, and the following two writing methods are feasible:

  • use @RequestParam or @PathVariable Receiving parameters (suitable for simple parameters)
     @GetMapping("test") public Object queryUser(@RequestParam String name, @RequestParam Integer age) { // do something... }
  • Use POJO to receive parameters (suitable for complex parameters)
     @GetMapping("test") public Object queryUser(@Validated UserParam userParam) { // do something... } record UserParam(@NotEmpty String name, Integer age) { }

    Note that the parameters are assembled by Spring in this case, and the annotation and processing of JSON in POJO will not take effect. If the field is an enumeration, you can use the org.springframework.core.convert.converter.Converter Interface custom wrapper type to enumeration conversion class.

In actual development, it is recommended to consider using the body in GET only when the following conditions are met:

  • API is only used between backend and backend
  • The URL parameter of API is difficult to construct or the URL is too long
  • Both API providers and users are familiar with HTTP and the network framework they use
  • There is no hierarchy between the API provider and the caller that will modify the transmission data (including gateway and proxy)

Length of URL

Streaming saving: suggestions No more than 2083 characters Otherwise, you need to negotiate with the client.

HTTP 1.1 protocol (RFC 7230 3.1.1 Chapter) does not specify the URL length limit. However, considering the restrictions between most clients (mainly browsers) and servers, press Internet Explorer URL length is limited to 2083 It is better as the upper limit of URL length.

Do you want to abandon GET?

When designing GET's query API, you may encounter situations that violate the above two principles (for example, using GET requests and using URLs to pass parameters leads to too long). At this time, it is necessary to consider whether the API parameter design is appropriate.

Another solution is that the client still uses POST to initiate requests, and at the same time X-HTTP-Method-Override The header tells the server which verb to use. Or give up designing according to RESTful API.

PUT or PATCH?

The main differences between the two are:

  • PUT means full update, idempotent
  • PATCH means local update, non idempotent

PATCH was added to the HTTP method in 2010. The main purpose is to solve the problem that the full transmission data is too large for each update. A small number of frameworks or tools do not support PATCH by default.

In addition, when PATCH is used, the JSON structure passed may be very different from the POST/PUT structure. therefore proposal The API provider and user negotiate in advance.

In some projects (such as GO), the PATCH method is used in combination with the JSON PATCH structure. See also this article: HTTP - PATCH method! I've been thinking wrong

Considering both the use and implementation of APIs, each has its own advantages. For example, when the front end uses Vue+Element UI to build editing pages, the PUT interface is easier to use. When the backend uses SpringMVC+MyBatisPlus, it is more consistent with the semantics of PATCH by default.

project should Select as required (usually one of two can be selected).

Can statelessness be "broken"?

No, statelessness is one of the design principles of REST. Moreover, statelessness can simplify the development difficulty for distributed applications.

Most "stateful" situations can be handled as follows:

  1. Convert the status to the requested parameter to eliminate the status (that is, the API itself is still stateless, such as the page number in the paging query)
  2. Save the status data on the client side instead of the server side by means of cookies

Response Data

In actual development, many teams will choose to design and use a unified response body to wrap the data returned by each API. For example, the following is a query response:

 { "Code": 1,//Business status code (generally used to indicate success or failure) "Message": "Success",//Status message "Data": {//The actual data node "account": "steve", "point": 100 } }

The following three situations may be encountered during development:

  1. Request processed successfully : The HTTP status code is two hundred , the business status code is Success, and the data is the actual data
  2. Business processing exception 🔴 : The HTTP status code is two hundred , the business status code is failed, and data may have no meaning
  3. Interface request exception : The HTTP status code is not two hundred , may not enter the business logic at all

Note: From the perspective of receiver semantics, in this case, the unified response body is regarded as a part of the response, rather than a layer of packaging. In other words, the above example is equivalent to:

 { "Code": 1,//Business status code "Message": "Success",//Status message "account": "steve", "point": 100 }

The unified response body can be used to express Business logic The success status of does not violate the REST style.

However, in the RESTful API, the status code of the HTTP response can be clearly expressed Call (request) Success status of. So you can also directly use the HTTP status code to indicate the success status, Rather than unified responder This is also a common usage (such as GitHub's API).

The API returns data directly when the response is successful; When the API response fails, the error code (business status code) and description are returned.

But there are also obvious disadvantages:

  • For browsers, non HTTP 2xx responses will be prompted in the developer tool's Network and Console
  • For some back-end tools (such as Spring's FeignClient )Unable to flexibly handle non HTTP 2xx responses
Back end and front end sample code

The implementation of the back end (SpringBoot) and front end (Axios) is not complicated

 //Controller @RestController public class TestController { @GetMapping("user-center/v1.1/user/{id}") public String queryUser(@PathVariable String id) { return "{\\"name\\":\\"Steve\\",\\"point\\":100}"; } } //Global exception handling @RestControllerAdvice public class CommonExceptionHandler { @ExceptionHandler(value = BusinessException.class) public ResponseEntity handleBusinessException(BusinessException e) { return ResponseEntity.status(HttpStatus. BAD_REQUEST). body (new Error (1001, "Unable to process the request")); } @ExceptionHandler(value = RuntimeException.class) public ResponseEntity handleOtherException(RuntimeException e) { return ResponseEntity.status(HttpStatus. INTERNAL_SERVER_ERROR). body (new error (1003, "server unknown exception")); } public static record Error(Integer errorCode, String description) {} }
 axios.request({ url: 'user-center/v1.1/user/10001', method: 'get', }).then(response => { const { name,  point } = response.data //Get the data returned by the server Console. log (` User: ${name} shares ${point} points `) }).catch(error => { if (error.response) { //The status code that the request was sent successfully and the server also responded to is out of the range of 2xx console.log(`HTTP ${error.response.status} (${error.response.statusText})`); //Get error description const { errorCode,  description } = response.data Console. log (` Error, reason: ${description}, error code: ${errorCode} `); } else { //There was a problem sending the request. It is usually a network exception console.log('Network Error', error.message) } });

Authentication (authority)

The authentication method needs to be friendly to the following aspects: cross domain, scaling/load balancing, so stateless authentication schemes such as JWT should be used instead of sessions. In fact, most RESTful APIs use OAuth 2.0 Conduct identity authentication.

In addition, in modern architecture (such as microservices), authentication can also be considered at the gateway, and applications will not be authenticated.

CORS (request cross domain)

This is not a problem caused by REST itself, but there are some noteworthy points when designing APIs. stay Microsoft REST API Design Guidelines Middle (Chapter 8), for compliance Design Guidelines The developed service has the following sentence:

Services SHOULD support an allowed origin of CORS * and enforce authorization through valid OAuth tokens. Services SHOULD NOT support user credentials with origin validation.

Reference translation:

Server should All cross site (CORS *) sources are supported, and authorization to use the OAuth token is mandatory. Server Should not User credentials with source authentication are supported.

Combined with the context of this chapter, this gives us two design inspirations:

  1. Use stateless, cross domain authentication scheme
  2. The server directly and completely opens CORS restrictions (the client does not need to handle CORS issues separately)

In addition, if the request requires cookies, the response header should be marked with Access-Control-Allow-Credentials And set to true

An example of SpringBoot
 @Component public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) response; //* indicates that cross domain access is allowed for all requests res.addHeader("Access-Control-Allow-Credentials", "true"); res.addHeader("Access-Control-Allow-Origin", "*"); res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN"); if (((HttpServletRequest) request).getMethod().equals(HttpMethod.OPTIONS.name())) { response.getWriter().println("Success"); return; } chain.doFilter(request, response); } }

Handling CORS pre checks

CORS policy may lead to Preflight , the client may need to avoid this situation (for example, the client only sends simple HTTP requests).

Example

When visiting Zhihu page, the first time the browser initiates Pre inspection (Request via OPTIONS)

The second time is the actual GET request

In addition, the server needs to Properly process the pre inspection request according to Design Guidelines Content in (Section 8.2)

If the request uses the OPTIONS method and contains the Access-Control-Request-Method header, then it is a preflight request intended to probe for access before the actual request. Otherwise, it is an actual request.

The following conditions can be regarded as pre inspection:

  1. Appears in the request header Origin
  2. Request using OPTIONS method
  3. Appears in the head Access-Control-Request-Method

For the pre check request, the server's response header needs to add the following:

  • Access-Control-Allow-Headers Header list allowed by the client (simple request header Accept Accept-Language Can be ignored). If there is no restriction, it can also be directly returned to the client request Access-Control-Request-Headers The value of the header.
  • Access-Control-Allow-Methods HTTP request methods that users are allowed to use, such as GET, POST, OPTIONS, etc

Limited space here, but more discussion Pre inspection Simple request and Complex request

In actual development, if each API is individually configured for pre checking (that is, to achieve a smaller granularity), the code may become complex. One Alternatives Yes, do the same for all APIs when receiving the pre check.

For example, when developing with SpringBoot, you can configure a Interceptor or Filter Unified processing. See the example in the previous section for the code.

Discard REST?

(Yes, I read the title correctly. This is a problem many developers are struggling with.)

It is not difficult to see from the previous content that it is sometimes difficult to design APIs in a REST style for various reasons. For example, the following situations are obviously not suitable for REST design:

  • The server must be compatible with some very old clients that only support GET/POST
  • The old back-end service API needs to be rebuilt on a large scale, or new APIs need to be developed for clients using the old technology

In this article, we will not discuss the content irrelevant to technology. As a service developer, sometimes you should consider some non-technical matters, such as development difficulty, efficiency, cost, etc.

Of course, in other cases, as long as there is no obvious contradiction with the REST style, you can consider developing as REST. In particular, it is beneficial to unify the style when developing large-scale projects.

Recommended reading

At the end of the article, some valuable materials are summarized for reference.

  1. Microsoft REST API design guidelines (guidelines): api-guidelines/Guidelines.md (Recommended for readers who want to study in depth)
  2. GitHub REST API instruction document: GitHub REST API - GitHub Docs (Excellent REST API examples)
  3. RESTful introduction (materials): RESTful API A popular API design style Understand what RESTful API is
  4. REST architecture style paper (original): Representational State Transfer (REST)

Finally, thanks for reading~

Unless otherwise specified, the content of this website is Salted fish pioneer Original, can be quoted freely, but please indicate the source and link.
https://xyuxf.com/archives/2181
Welcome to follow Salted fish pioneer (WeChat official account: xyuxf), get dry goods push
THE END
share
QR code
< <Previous
Next>>
Article Contents
close
catalog