Build Restful Web Services

1. Understand REST

  the full name of REST is Representational State Transfer, which means Representational State Transfer in Chinese. It first appeared in Roy Fielding's doctoral thesis in 2000. Roy fielding is one of the main writers of HTTP specification. It is worth noting that REST does not have a clear standard, but is more like a design style. If an architecture conforms to the constraints and principles of REST, we call it RESTful architecture.

  theoretically, the REST architecture style is not bound to HTTP, but HTTP is the only instance related to REST.

1.1. REST principles

  • Resources can be exposed through URIs of directory structure style
  • Expressions can be passed through data objects or attributes expressed in JSON or XML
  • Messages use unified HTTP methods (for example, GET, POST, PUT, and DELETE)
  • The interaction between stateless client and server is stateless between requests. Each request from client to server must contain the information necessary to understand the request

    1.2. HTTP method

      use HTTP to map CRUD (create, retrieve, update, DELETE < create, obtain, update, DELETE - add, DELETE, modify query >) operations to HTTP requests. If resources are exposed according to the semantics of HTTP methods, the interface will have the characteristics of security and idempotence. For example, GET and HEAD requests are secure, and the server state will not be changed no matter how many times they are requested. The GET, HEAD, PUT and DELETE requests are idempotent. No matter how many times the resource is operated, the result is always the same. The subsequent requests will not have more impact than the first time.

    1.2.1. GET

  • Secure and idempotent
  • pick up information

    1.2.2. POST

  • Unsafe and not idempotent
  • Use the entity provided in the request to perform operations that can be used to create or update resources

    1.2.3. PUT

  • Unsafe but idempotent
  • Use the entity provided in the request to perform operations that can be used to create or update resources

    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, add a java category to my blog, and the generated path is the category name / categories/java. Then you can use the PUT method. However, many people directly map POST, GET, PUT and DELETE to CRUD. For example, this is done 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: successful
  • 3XX: forwarding
  • 4XX: client error
  • 5XX: server error

    1.4. media type

       Accept and content type in HTTP header can be used to describe the content sent or requested in HTTP request. If the client requests a JSON response, you can set Accept to application/json. Accordingly, if the sent content is XML, you can set the content type to application/xml.

    2. REST API design best practices

      here are some best practices for designing rest APIs. Let's remember the following sentence first:

    A URL is a sentence in which a resource is a noun and an HTTP method is a verb.

    2.1. Use nouns to denote resources

      here are some examples:

  • GET - /users: returns the 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
      do not use verbs:
  • /getAllsers
  • /getUserById
  • /createNewUser
  • /updateUser
  • /deleteUser

    2.2 use appropriate serialization format in HTTP header

      both the client and the server need to know the format used for communication. This format should be specified in the HTTP header:

  • Content type defines the request format
  • Accept defines a list of acceptable response formats

    2.3 Get method and query parameters should not change state

      use PUT, POST and DELETE methods to change the state. Do not use GET methods to change the state:

  • GET /users/711?activate or
  • GET /users/711/activate

    2.4. Use child resources to represent associations

       if a resource is associated with another resource, use sub resources:

  • GET /cars/711/drivers / return the driver list of car 711
  • GET /cars/711/drivers/4 return to driver 4 of car 711

    2.5. Use the appropriate HTTP method (verb)

      let's review this sentence again:

    A URL is a sentence in which a resource is a noun and an HTTP method is a verb.

  • GET: GET the expression specified in the URI resource, and the response message body contains the details of the requested resource.
  • POST: create a new resource specified by the URI and request the message body to provide the details of the new resource. Note that POST can also trigger some operations, not necessarily to create a new resource.
  • PUT: creates or replaces a resource with a specified URI. The request message body specifies the resource to create or update.
  • DELETE: removes the resource with the specified URI.

    2.6. HTTP response status code

       when the client sends a request to the server through the API, the client should know the feedback: whether it fails, passes or the request is wrong. HTTP status codes are a batch of standardized codes with different interpretations in different scenarios. The server should always return the correct status code.
      the following are important HTTP code categories:

  • 2xx (success classification): these status code request actions are received and successfully processed by the server.

    • 200: Ok indicates the standard status code of GET, PUT or POST requests.
    • 201: Created indicates that the instance has been Created, which is mostly used for POST methods.
    • 204: No Content indicates that the request has been successfully processed but No Content has been returned. Commonly used in DELETE method returns.
  • 3xx (forwarding classification)

    • 304: Not Modified indicates 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 an error request.

    • 400: Bad Request indicates that the client request has not been processed because the server cannot understand the client request.
    • 401: Unauthorized means that the client does not have permission to access resources. It should add the required authentication information and request again.
    • 403: Forbidden indicates that the request is valid and the client is authorized, but the client does not have access to the resource.
    • 404: Not Found indicates that the requested resource is not available now.
    • 410: Gone indicates that the requested resource has been removed.
  • 5xx (server error classification)

    • 500: Internal Server Error indicates that the request is valid, but the server has an exception.
    • 503: Service Unavailable indicates that the server is down or unavailable. It usually means that the server is in maintenance status.

    2.7. Name Convention

      you can follow any name Convention as long as you maintain cross application consistency. If the request body and response body are JSON types, please follow the hump name convention.

    2.8. Search, sorting, filtering and paging

       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. Here are some examples:

  • Sorting: in this example, if the customer wants to get the sorted company list, GET /companies should accept multiple sorting parameters during query. For example, GET /companies?sort=rank_asc will sort companies in ascending order.
  • Filtering: to filter data sets, we can pass different options through query parameters. Like get / companies? Category = Banking & location = India Filters Companies classified as banks and located in India.
  • Search: the API endpoint for searching the 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 is helpful to improve the performance of the server and facilitate the client to process the response. Such as GET /companies?page=23 means to obtain the data on page 23 of the company list.

    2.9. Restful API version

       generally, simple numbers without dots are used to represent the version, and the letter v before the number represents the version number, as shown below:

  • /blog/api/v1
  • http://api.yourservice.com/v1...

    2.10. Handle JSON error body

      API error handling mechanism is very important and should be well planned. It is highly recommended that you always include an error message in the return field. A JSON error body should provide developers with some useful information: error message, error code and detailed description. The following 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

      the following format of URL is recommended:

  • http(s): / / {domain name (: port number)} / {indicates the value of the REST API} / {API version} / {identifies the path of the resource}
  • http(s): / / {indicates the domain name of the REST API (: port number)} / {API version} / {identifies the path of the resource}
       as follows:
  • http://example.com/api/v1/mem...
  • http://api.example.com/v1/mem...

    3. Develop Restful Web services based on Spring Boot

      Spring Boot provides excellent support for building RESTful Web services in enterprise applications.

    3.1. Introduce dependency

      to build RESTful Web services, we need to add the Spring Boot Starter Web dependency to the build configuration file.
      for Maven users, use the following code in POM Add dependency to XML file:

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>    
    </dependency>

      for Gradle users, use the following code in build Add dependency to 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 be familiar with the following comments:

    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 Annotation is used to define the content type of the request body.
    public ResponseEntity<Object> createProduct(@RequestBody Product product) {
    }

    Path Variable

       @ PathVariable annotation is used to define custom or dynamic request URI, and Path variable is placed in braces in the request URI, as shown below:

    public ResponseEntity<Object> updateProduct(@PathVariable("id") String id) {
    }

    Request Parameter

    The     @ RequestParam annotation is used to read request parameters from the request URL. It is required by default. You can also set default values for request parameters. As follows:
    public ResponseEntity<Object> getProduct(
    @RequestParam(value = "name", required = false, defaultValue = "honey") String name) {
    }

    3.3. Writing rest APIs

    GET API

      the following example code defines the HTTP GET request method. In this example, we use HashMap to store Product in. Note that we use the POJO class to store products.
      here, the request URI is / products, which will return the product list from the HashMap repository. The following controller class file contains the REST endpoint of 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 URL s by sending request parameters and path variables.
      the following example code defines the HTTP POST request method. In this example, we use HashMap to store Product, which is a POJO class.
       here, the request URI is / products. After the product is stored in the HashMap warehouse, it will return a string.

    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 URL s by sending request parameters and path variables.
      the following example shows how to define the HTTP PUT request method. In this example, we use HashMap to update the existing product. Here, the product is a POJO class.
       here, the request URI is / products/{id}. After the product is stored in the HashMap warehouse, it will return a string. Note that we use the path variable {ID} to define the product ID 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

      HTTP Delete request is used to delete existing resources. This method does not contain any request body. We can define custom or dynamic URL s by sending request parameters and path variables.
      the following example shows how to define the HTTP DELETE request method. In this example, we use HashMap to remove the existing product.
       the request URI is / products/{id}. It will return a string after the product is deleted 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);
     }
    }

Keywords: RESTful

Added by PugJr on Wed, 09 Mar 2022 05:25:04 +0200