JavaRush /Java Blog /Random EN /REST overview. Part 3: Creating a RESTful Service with Sp...

REST overview. Part 3: Creating a RESTful Service with Spring Boot

Published in the Random EN group
This is the final part of the REST parsing. In previous parts: REST overview.  Part 3: Creating a RESTful Service with Spring Boot - 1

Create a project

In this section, we will create a small RESTful Spring Boot application. Our application will implement CRUD (Create, Read, Update, Delete) operations on clients from the example from the last part of the analysis. First, let's create a new Spring Boot application through the menu File -> New -> Project... In the window that opens, select Spring Initializr and specify the Project SDK: REST overview.  Part 3: Creating a RESTful Service with Spring Boot - 2Press the Next button. In the next window, specify the Maven project type, specify Group and Artifact: REST overview.  Part 3: Building a RESTful Service with Spring Boot - 3Press the Next button. In the next window, we need to select the Spring Framework components we need for the project. Spring Web will be enough for us: REST overview.  Part 3: Creating a RESTful Service with Spring Boot - 4Click the Next button. Then it remains to specify only the name of the project and its location in the file system: REST overview.  Part 3: Building a RESTful Service with Spring Boot - 5Click the Finish button. The project is created, now we can see its structure: REST overview.  Part 3: Creating a RESTful Service with Spring Boot - 6IDEA generated the Maven build system deployment descriptor for us - pom.xml and the main application class: RestExampleApplication. Here is their code:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.2.2.RELEASE</version>
       <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.codegym.lectures</groupId>
   <artifactId>rest_example</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>rest_example</name>
   <description>REST example project</description>

   <properties>
       <java.version>1.8</java.version>
   </properties>

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

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
           <exclusions>
               <exclusion>
                   <groupId>org.junit.vintage</groupId>
                   <artifactId>junit-vintage-engine</artifactId>
               </exclusion>
           </exclusions>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
   </build>

</project>
RestExampleApplication:
@SpringBootApplication
public class RestExampleApplication {

   public static void main(String[] args) {
       SpringApplication.run(RestExampleApplication.class, args);
   }

}

Creation of REST functionality

Our application manages clients. Therefore, the first thing we need to do is create a client entity. This will be the POJO class. Let's create a package modelinside a package com.codegym.lectures.rest_example. modelLet's create a class inside the package Client:
public class Client {

   private Integer id;
   private String name;
   private String email;
   private String phone;

   public Integer getId() {
       return id;
   }

   public void setId(Integer id) {
       this.id = id;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getEmail() {
       return email;
   }

   public void setEmail(String email) {
       this.email = email;
   }

   public String getPhone() {
       return phone;
   }

   public void setPhone(String phone) {
       this.phone = phone;
   }
}
The service will implement CRUD operations on the client. The next step is to create a service that will implement these operations. com.codegym.lectures.rest_exampleLet's create a package in the package serviceand create an interface inside it ClientService. Here is the interface code with comments:
public interface ClientService {

   /**
    * Создает нового клиента
    * @param client - клиент для создания
    */
   void create(Client client);

   /**
    * returns список всех имеющихся клиентов
    * @return список клиентов
    */
   List<client> readAll();

   /**
    * returns клиента по его ID
    * @param id - ID клиента
    * @return - an object клиента с заданным ID
    */
   Client read(int id);

   /**
    * Обновляет клиента с заданным ID,
    * в соответствии с переданным клиентом
    * @param client - клиент в соответсвии с которым нужно обновить данные
    * @param id - id клиента которого нужно обновить
    * @return - true если данные были обновлены, иначе false
    */
   boolean update(Client client, int id);

   /**
    * Удаляет клиента с заданным ID
    * @param id - id клиента, которого нужно удалить
    * @return - true если клиент был удален, иначе false
    */
   boolean delete(int id);
}
Next, we need to create an implementation of this interface. Now it will act as a customer storage Map<Integer, Client>. The map key will be the client id, and the value will be the client itself. This was done in order not to overload the example with the specifics of working with the database. However, in the future, we will be able to write another implementation of the interface, in which it will be possible to connect a real database. serviceLet's create an implementation of the interface in the package ClientService:
@Service
public class ClientServiceImpl implements ClientService {

   // Хранorще клиентов
   private static final Map<Integer, Client> CLIENT_REPOSITORY_MAP = new HashMap<>();

   // Переменная для генерации ID клиента
   private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();

   @Override
   public void create(Client client) {
       final int clientId = CLIENT_ID_HOLDER.incrementAndGet();
       client.setId(clientId);
       CLIENT_REPOSITORY_MAP.put(clientId, client);
   }

   @Override
   public List<Client> readAll() {
       return new ArrayList<>(CLIENT_REPOSITORY_MAP.values());
   }

   @Override
   public Client read(int id) {
       return CLIENT_REPOSITORY_MAP.get(id);
   }

   @Override
   public boolean update(Client client, int id) {
       if (CLIENT_REPOSITORY_MAP.containsKey(id)) {
           client.setId(id);
           CLIENT_REPOSITORY_MAP.put(id, client);
           return true;
       }

       return false;
   }

   @Override
   public boolean delete(int id) {
       return CLIENT_REPOSITORY_MAP.remove(id) != null;
   }
}
The annotation @Servicetells Spring that the given class is a service. This is a special type of class that implements some application business logic. Subsequently, thanks to this annotation, Spring will provide us with an instance of this class in places where it is needed using Dependency Injection. Now it's time to create the controller. A special class in which we implement the logic for processing client requests for endpoints (URIs). To make it clearer, we will create this class in parts. First, let's create the class itself and inject a dependency on it into it ClientService:
@RestController
public class ClientController {

   private final ClientService clientService;

   @Autowired
   public ClientController(ClientService clientService) {
       this.clientService = clientService;
   }
}
Let's explain the annotations: @RestController tells Spring that this class is a REST controller. Those. this class will implement the logic for processing client requests @Autowired - it tells spring that it is necessary to inject the dependency at this point. We pass the interface to the constructor ClientService. We marked the implementation of this service with an annotation @Serviceearlier, and now spring will be able to pass an instance of this implementation to the controller constructor. Next, we will step by step implement each controller method to handle CRUD operations. Let's start with the Create operation. Let's write a method for this create:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
   clientService.create(client);
   return new ResponseEntity<>(HttpStatus.CREATED);
}
Let's analyze this method: @PostMapping(value = "/clients")- here we mean that this method processes POST requests to the address /clients The method returns ResponseEntity<?>. ResponseEntity- a special class for returning responses. With it, we will be able to return the status code to the HTTP client in the future. The method takes a parameter @RequestBody Client client, the value of this parameter is substituted from the request body. The abstract says so @RequestBody. Inside the method body, we call the create method on the previously created service and pass it the client controller passed in the parameters. After that, we return the 201 Created status by creating a new object ResponseEntityand passing the desired enum value to it HttpStatus. Next, implement the operation Read: First, implement the operation of obtaining a list of all available clients:
@GetMapping(value = "/clients")
public ResponseEntity<List<Client>> read() {
   final List<Client> clients = clientService.readAll();

   return clients != null &&  !clients.isEmpty()
           ? new ResponseEntity<>(clients, HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Let's start parsing: @GetMapping(value = "/clients")- everything is similar to the annotation @PostMapping, only now we process GET requests. This time we return ResponseEntity<List<Client>>, only this time, in addition to the HTTP status, we will also return the response body, which will be a list of clients. In REST spring controllers, all POJOs, as well as collections of POJOs that are returned as response bodies, are automatically serialized to JSON unless otherwise specified. We are quite satisfied with this. Inside the method, using our service, we get a list of all clients. Further, if the list is not null and not empty, we return ResponseEntitythe client list itself and HTTP status 200 OK using the class. Otherwise, we simply return a 404 Not Found HTTP status. Next, we implement the ability to get a client by its id:
@GetMapping(value = "/clients/{id}")
public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
   final Client client = clientService.read(id);

   return client != null
           ? new ResponseEntity<>(client, HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
From the new, we have a path variable here. A variable that is defined in a URI. value = "/clients/{id}". We have indicated it in curly brackets. And in the method parameters, we accept it as a intvariable, using the annotation @PathVariable(name = "id"). This method will accept requests for a uri of the form /clients/{id}, where instead {id}it can be any numerical value. This value is subsequently passed to a variable int id- the method parameter. In the body, we get the object Clientusing our service and the accepted id. And then, by analogy with the list, we return either the 200 OK status and the object itself Client, or simply the 404 Not Found status, if there idis no client with this in the system. It remains to implement two operations - Update and Delete. Here is the code for these methods:
@PutMapping(value = "/clients/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
   final boolean updated = clientService.update(client, id);

   return updated
           ? new ResponseEntity<>(HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}

@DeleteMapping(value = "/clients/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
   final boolean deleted = clientService.delete(id);

   return deleted
           ? new ResponseEntity<>(HttpStatus.OK)
           : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
There is nothing essentially new in these methods, so we will skip the detailed description. The only thing worth mentioning is that the method updatehandles PUT requests (annotation @PutMapping), while the method deletehandles DELETE requests (annotation DeleteMapping). Here is the complete controller code:
@RestController
public class ClientController {

   private final ClientService clientService;

   @Autowired
   public ClientController(ClientService clientService) {
       this.clientService = clientService;
   }

   @PostMapping(value = "/clients")
   public ResponseEntity<?> create(@RequestBody Client client) {
       clientService.create(client);
       return new ResponseEntity<>(HttpStatus.CREATED);
   }

   @GetMapping(value = "/clients")
   public ResponseEntity<List<Client>> read() {
       final List<client> clients = clientService.readAll();

       return clients != null &&  !clients.isEmpty()
               ? new ResponseEntity<>(clients, HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_FOUND);
   }

   @GetMapping(value = "/clients/{id}")
   public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
       final Client client = clientService.read(id);

       return client != null
               ? new ResponseEntity<>(client, HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_FOUND);
   }

   @PutMapping(value = "/clients/{id}")
   public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
       final boolean updated = clientService.update(client, id);

       return updated
               ? new ResponseEntity<>(HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
   }

   @DeleteMapping(value = "/clients/{id}")
   public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
       final boolean deleted = clientService.delete(id);

       return deleted
               ? new ResponseEntity<>(HttpStatus.OK)
               : new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
   }
}
As a result, the structure of our project looks like this: REST overview.  Part 3: Creating a RESTful Service with Spring Boot - 7REST overview.  Part 3: Creating a RESTful Service with Spring Boot - 8

Launch and testing

To run our application, it is enough to run a method mainin the RestExampleApplication. And in order to test RESTful web services, you need to download new software) The fact is that GET requests are quite easy to send from a regular browser, but for POST, PUT and DELETE a regular browser is indispensable. Don't worry, you can use Postman to send any HTTP requests. You can download it from here . After downloading and installing, we start testing our application. To do this, open the program and create a new request: REST overview.  Part 3: Building a RESTful Service with Spring Boot - 9Press the New button in the upper left corner. Next, select Request: REST overview.  Part 3: Building a RESTful Service with Spring Boot - 10Next, give it a name and save it. Now let's try to send a POST request to the server and create the first client: REST overview.  Part 3: Building a RESTful Service with Spring Boot - 11We create several clients in this way. Then we change the request type to GET and send it to the server: REST overview.  Part 3: Building a RESTful Service with Spring Boot - 12

General results

Congratulations: we've covered quite the topic of REST. All the material turned out to be voluminous, but we hope it will be useful for you:
  1. We learned what REST is.

  2. We got acquainted with the history of the emergence of REST.

  3. We talked about the limitations and principles of this architectural style:

    • bringing the architecture to the client-server model;
    • lack of state;
    • caching;
    • interface uniformity;
    • layers;
    • code on demand (optional constraint).
  4. Analyzed the benefits that REST gives

  5. We examined in detail how the server and client interact with each other via the HTTP protocol.

  6. Take a closer look at the requests and answers. Dismantled their components.

  7. Finally, we moved on to practice and wrote our own small RESTful application on Spring Boot. And even learned how to test it using the Postman program.

Phew. It turned out to be voluminous, but, nevertheless, there is something to do as homework.

Homework

Try to do the following:
  1. Following the description above, create your own Spring Boot project and implement the same logic in it as in the lecture. Repeat all 1 in 1.
  2. Run. application.
  3. Download and set up Postman (or any other tool for sending requests, even curl).
  4. Test POST and GET requests in the same way as it was mentioned in the lecture.
  5. Test PUT and DELETE requests yourself.
Part 1: What is REST Part 2: Communication between client and server
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION