1. Understand REST
The full name of REST is Representational State Transfer, which means representational state transfer in Chinese. It first appeared in 2000 in the doctoral dissertation of Roy Fielding, one of the main writers of the HTTP specification. It is worth noting that REST does not have a clear standard, but more of a design style. If an architecture conforms to the constraints and principles of REST, we call it a RESTful architecture.
In theory, the REST architectural style is not bound to HTTP, but HTTP is currently the only instance related to REST.
1.1. REST principles
- Resources are exposed via directory-structured URIs
- Representations can be passed through data objects or properties expressed in JSON or XML
- Messages use uniform HTTP methods (eg: GET, POST, PUT, and DELETE)
The interaction between a stateless client and server is stateless between requests, and each request from the client to the server must contain the information necessary to understand the request
1.2. HTTP Methods
Use HTTP to map CRUD (create, retrieve, update, delete <create, fetch, update, delete—add, delete, modify and check>) operations to HTTP requests. If the resource is exposed according to the semantics of the HTTP method, then the interface will have the characteristics of safety and idempotency, such as GET and HEAD requests are safe, no matter how many times the request is made, the server state will not be changed. GET, HEAD, PUT and DELETE requests are all idempotent. No matter how many times the resource is operated, the result is always the same, and subsequent requests will not have more impact than the first one.
1.2.1. GET
- Safe and idempotent
getting information
1.2.2. POST
- Unsafe and not idempotent
Performs an action using the entity provided in the request, which can be used to create a resource or update a resource
1.2.3. PUT
- Unsafe but idempotent
Performs an action using the entity provided in the request, which can be used to create a resource or update a resource
1.2.4. DELETE
- Unsafe but idempotent
delete resource
The difference between POST and PUT in creating resources is whether the name (URI) of the created resource is determined by the client. For example, adding a java category to my blog post, the generated path is the category name/categories/java, then the PUT method can be used. However, many people directly map POST, GET, PUT, and DELETE to CRUD, for example, in a typical RESTful application implemented by rails.1.3. HTTP status codes
The status code indicates the result of the HTTP request:
- 1XX: Information
- 2XX: success
- 3XX: Forward
- 4XX: Client Error
5XX: Server error
1.4. Media Types
Accept and Content-Type in HTTP headers can be used to describe what is sent or requested in an HTTP request. If the client requests a JSON response, Accept can be set to application/json. Correspondingly, if the content to be sent is XML, the Content-Type can be set to application/xml.
2. REST API Design Best Practices
Here are some best practices for designing REST APIs, let's remember the following sentence:
A URL is a sentence where resources are nouns and HTTP methods are verbs.
2.1. Using nouns to represent resources
Here are some examples:
- GET - /users: returns a list of users
- GET - /users/100: returns a specific user
- POST - /users: create a new user
- PUT - /users/200: Update a specific user
- DELETE - /users/711: delete a specific user
Don't use verbs: /getAllsers/getUserById/createNewUser/updateUser/deleteUser2.2 Use proper serialization format in HTTP headers
Both the client and the server need to know the format used for communication, which is specified in the HTTP header:
- Content-Type defines the request format
Accept defines a list of acceptable response formats
2.3 Get methods and query parameters should not change state
Use PUT, POST and DELETE methods to change state, do not use GET method to change state:
- GET
/users/711?activateor GET
/users/711/activate2.4. Using subresources to represent associations
If a resource is associated with another resource, use subresources:
- GET /cars/711/drivers/ returns a list of drivers for car 711
GET /cars/711/drivers/4 returns driver number 4 of car number 711
2.5. Use the appropriate HTTP method (verb)
Review this sentence again:
A URL is a sentence where resources are nouns and HTTP methods are verbs.
- GET : Get the representation specified in the URI resource, the response message body contains the details of the requested resource.
- POST : Creates a new resource specified by a URI, requesting the body to provide details of the new resource. Note that POST can also trigger some actions, not necessarily creating a new resource.
- PUT : Create or replace the resource with the specified URI. The request message body specifies the resource to be created or updated.
DELETE : Removes the resource at the specified URI.
2.6. HTTP Response Status Codes
When the client initiates a request to the server through the API, the client should know the feedback: whether it failed, passed, or the request was wrong. HTTP status codes are a group of standardized codes that have different interpretations in different scenarios. The server should always return the correct status code.
The following are important HTTP code classifications:2xx (successful classification): These status code codes request the action to be received and successfully processed by the server.
- 200: Ok represents the standard status code for a GET, PUT or POST request.
- 201: Created (created) indicates that the instance has been created, mostly used in the POST method.
- 204: No Content indicates that the request was successfully processed but no content was returned. Often used for DELETE method returns.
3xx (Forward classification)
- 304: Not Modified means that the client has cached this response and does not need to transmit the same content again.
4xx (Client Error Classification): These status codes represent that the client submitted a bad request.
- 400: Bad Request indicates that the client request was not processed because the server could not understand the client request.
- 401: Unauthorized indicates that the client has no right to access the resource, and should request it again after adding the required authentication information.
- 403: Forbidden indicates that the request is valid and the client is authorized, but the client does not have permission to access the resource.
- 404: Not Found (not found) indicates that the requested resource is not available now.
- 410: Gone indicates that the requested resource has been removed.
5xx (server misclassification)
- 500: Internal Server Error (internal server error) indicates that the request is valid, but an exception occurred on the server side.
- 503: Service Unavailable (service unavailable) indicates that the server is down or unavailable, which usually means that the server is in a maintenance state.
2.7. Name convention
You can follow any naming convention as long as it is consistent across applications. If the request body and response body are of type JSON, follow the camel case name convention.
2.8. Searching, sorting, filtering and pagination
Some of the above examples are simple queries on a dataset. For complex data, we need to add some parameters to the GET method API to process. Here are some examples:
- Sorting: In this example, the client wants to get a sorted list of companies, GET /companies should accept various sorting parameters in the query. For example GET /companies?sort=rank_asc will sort companies in ascending rank order.
- Filtering: To filter the dataset, we can pass different options through query parameters. For example GET /companies?category=banking&location=india would filter for companies that are classified as banks and located in India.
- Search: The API endpoint to search for a company name in the company list should be GET /companies?search=Digital.
Paging: When the data set is too large, we should divide the data set into small data blocks, which will help improve the performance of the server and facilitate the client to process the response. For example, GET /companies?page=23 means to get the data on page 23 of the companies list.
2.9. Restful API version
Generally, a simple number without a dot is used to indicate the version, and the letter v before the number represents the version number, as shown below:
- /blog/api/v1
http://api.yourservice.com/v1/companies/34/employees
2.10. Handling JSON error bodies
API error handling mechanisms are important and well planned. It is highly recommended to always include error messages in the return field. A JSON error body should provide the developer with some useful information: the error message, the error code, and a detailed description. Here is a good example:
{ "code": 1234, "message": "Something bad happened :(", "description": "More details about the error here" }
2.11. How to create a Rest API URL
It is recommended to use URLs in the following format:
- http(s)://{domain name(:port number)}/{value representing REST API}/{API version}/{path to identify resource}
- http(s)://{represents the domain name (:port number) of the REST API}/{API version}/{path to identify the resource}
As follows: - http://example.com/api/v1/members/M000000001
http://api.example.com/v1/members/M000000001
3. Develop Spring Boot-based Restful Web Services
Spring Boot provides excellent support for building RESTful Web services in enterprise applications.
3.1. Introducing dependencies
To build a RESTful web service, we need to add the Spring Boot Starter web dependency to the build configuration file.
For Maven users, use the following code to add dependencies in the pom.xml file:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
For Gradle users, use the following code to add dependencies to the build.gradle file:
compile('org.springframework.boot:spring-boot-starter-web')
3.2. Rest related notes
Before continuing to build RESTful web services, it is recommended that you become familiar with the following annotations:
Rest Controller
The @RestController annotation is used to define RESTful web services. It provides JSON, XML and custom responses. The syntax is as follows:
@RestController public class ProductServiceController { }
Request Mapping
The @RequestMapping annotation is used to define the request URI to access the REST endpoint. We can define the Request method to consume the produce object. The default request method is GET:
@RequestMapping(value = "/products") public ResponseEntity<Object> getProducts() { } Request Body @RequestBody 注解用于定义请求体内容类型。 public ResponseEntity<Object> createProduct(@RequestBody Product product) { }
Path Variable
The @PathVariable annotation is used to define custom or dynamic request URIs. Path variables are placed within curly brackets in the request URI as follows:
public ResponseEntity<Object> updateProduct(@PathVariable("id") String id) { }
Request Parameter
The @RequestParam annotation is used to read request parameters from the request URL. Required by default, and default values can also be set for request parameters. As follows:
public ResponseEntity<Object> getProduct(
@RequestParam(value = "name", required = false, defaultValue = "honey") String name) {
}3.3. Writing a REST API
GET API
The following sample code defines the HTTP GET request method. In this example, we use HashMap to store Product. Notice that we used a POJO class to store the products.
Here, the request URI is /products, which returns a list of products from the HashMap repository. The following controller class file contains the REST endpoint for the GET method:package com.tutorialspoint.demo.controller; import java.util.HashMap; import java.util.Map; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.tutorialspoint.demo.model.Product; @RestController public class ProductServiceController { private static Map<String, Product> productRepo = new HashMap<>(); static { Product honey = new Product(); honey.setId("1"); honey.setName("Honey"); productRepo.put(honey.getId(), honey); Product almond = new Product(); almond.setId("2"); almond.setName("Almond"); productRepo.put(almond.getId(), almond); } @RequestMapping(value = "/products") public ResponseEntity<Object> getProduct() { return new ResponseEntity<>(productRepo.values(), HttpStatus.OK); } }
POST API
HTTP POST requests are used to create resources. This method contains the request body. We can define custom or dynamic URLs by sending request parameters and path variables.
The following sample code defines the HTTP POST request method. In this example, we use a HashMap to store Product, where Product is a POJO class.
Here, the request URI is /products, which returns a string after the product is stored in the HashMap repository.package com.tutorialspoint.demo.controller; import java.util.HashMap; import java.util.Map; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.tutorialspoint.demo.model.Product; @RestController public class ProductServiceController { private static Map<String, Product> productRepo = new HashMap<>(); @RequestMapping(value = "/products", method = RequestMethod.POST) public ResponseEntity<Object> createProduct(@RequestBody Product product) { productRepo.put(product.getId(), product); return new ResponseEntity<>("Product is created successfully", HttpStatus.CREATED); } }
PUT API
HTTP PUT requests are used to update existing resources. This method contains the request body. We can define custom or dynamic URLs by sending request parameters and path variables.
The following example shows how to define the HTTP PUT request method. In this example, we use a HashMap to update an existing product. Here, Product is a POJO class.
Here, the request URI is /products/{id}, which returns a string after the product is stored in the HashMap repository. Note that we use the path variable {id} to define the product ID that needs to be updated:package com.tutorialspoint.demo.controller; import java.util.HashMap; import java.util.Map; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.tutorialspoint.demo.model.Product; @RestController public class ProductServiceController { private static Map<String, Product> productRepo = new HashMap<>(); @RequestMapping(value = "/products/{id}", method = RequestMethod.PUT) public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody Product product) { productRepo.remove(id); product.setId(id); productRepo.put(id, product); return new ResponseEntity<>("Product is updated successsfully", HttpStatus.OK); } }
DELETE API
The HTTP Delete request is used to delete existing resources. This method does not contain any request body. We can define custom or dynamic URLs by sending request parameters and path variables.
The following example shows how to define the HTTP DELETE request method. In this example, we use a HashMap to remove existing products, represented by POJOs.
The request URI is /products/{id} and it returns the string after the product is removed from the HashMap repository. We use the path variable {id} to define the product ID to be deleted.package com.tutorialspoint.demo.controller; import java.util.HashMap; import java.util.Map; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.tutorialspoint.demo.model.Product; @RestController public class ProductServiceController { private static Map<String, Product> productRepo = new HashMap<>(); @RequestMapping(value = "/products/{id}", method = RequestMethod.DELETE) public ResponseEntity<Object> delete(@PathVariable("id") String id) { productRepo.remove(id); return new ResponseEntity<>("Product is deleted successsfully", HttpStatus.OK); } }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。