JavaRush /Java Blog /Random EN /Overview of REST. Part 3: Creating a RESTful Service in S...

Overview of REST. Part 3: Creating a RESTful Service in Spring Boot

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

Creating a Project

In this section, we will create a small RESTful application using Spring Boot. 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 Project SDK: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 2Click the Next button. In the next window, specify the Maven project type, specify Group and Artifact: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 3Click the Next button. In the next window, we need to select the Spring Framework components needed for the project. Spring Web will be enough for us: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 4Click the Next button. Next, all that remains is to specify the name of the project and its location in the file system: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 5Click the Finish button. The project has been created, now we can see its structure: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 6IDEA has generated for us the Maven build system deployment descriptor - pom.xml and the main application class: RestExampleApplication. Here's 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.javarush.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);
   }

}

Creating REST functionality

Our application manages clients. So the first thing we need to do is create a customer entity. This will be a POJO class. Let's create a package modelinside a package com.javarush.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. In the package com.javarush.lectures.rest_examplewe will create a package service, inside which we will create an interface 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 client repository Map<Integer, Client>. The card 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. In the package servicewe will create an implementation of the interface 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 this class is a service. This is a special type of class in which some business logic of the application is implemented. 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 introduce a dependency on it ClientService:
@RestController
public class ClientController {

   private final ClientService clientService;

   @Autowired
   public ClientController(ClientService clientService) {
       this.clientService = clientService;
   }
}
Let's clarify the annotations: @RestController - tells Spring that this class is a REST controller. Those. this class will implement the logic for processing client requests @Autowired - tells Spring that a dependency needs to be injected in this place. 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 process CRUD operations. Let's start with the Create operation. To do this, let's write a method create:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
   clientService.create(client);
   return new ResponseEntity<>(HttpStatus.CREATED);
}
Let's look at this method: @PostMapping(value = "/clients")- here we mean that this method processes POST requests to the /clients address. The method returns ResponseEntity<?>. ResponseEntity- a special class for returning responses. Using it, we can later return the HTTP status code to the client. The method takes a parameter @RequestBody Client client, the value of this parameter is substituted from the request body. The abstract speaks about this @RequestBody. Inside the body of the method, we call the create method on the previously created service and pass it the client controller accepted in the parameters. Then we return the status 201 Created by creating a new object ResponseEntityand passing the required enum value to it HttpStatus. Next, we implement the operation Read: First, we 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 the analysis: @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 Spring REST controllers, all POJO objects, as well as collections of POJO objects that are returned as response bodies, are automatically serialized to JSON unless explicitly specified otherwise. We are quite happy with this. Inside the method, using our service, we get a list of all clients. Next, if the list is not null or empty, we return ResponseEntitythe list of clients and the HTTP status 200 OK using the class. Otherwise, we simply return the HTTP status 404 Not Found. Next, we will implement the ability to receive a client by his 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);
}
Something new is that we now have a path variable. A variable that is defined in a URI. value = "/clients/{id}". We indicated it in curly braces. And in the method parameters we accept it as inta variable using the annotation @PathVariable(name = "id"). This method will accept requests for uri of the form /clients/{id}, where instead {id}there can be any numeric value. This value is subsequently passed to a variable int id—the method parameter. In the body we receive the object Clientusing our service and 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 a detailed description. The only thing worth mentioning is that the method updateprocesses PUT requests (annotation @PutMapping), and the method deleteprocesses 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: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 7

Launch and testing

To run our application, just run the method mainin the class 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 not enough. Don't worry: you can use Postman to send any HTTP requests. You can download it from here . After downloading and installing, we begin testing our application. To do this, open the program and create a new request: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 9Click the New button in the upper left corner. Next, select Request: Overview of REST.  Part 3: Creating a RESTful Service with Spring Boot - 10Next, give it a name and save it. Let's now try to send a POST request to the server and create the first client: Overview of REST.  Part 3: Creating a RESTful Service in Spring Boot - 11We create several clients in this way. Then we change the request type to GET and send it to the server: Overview of REST.  Part 3: Creating a RESTful Service in 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 REST.

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

    • bringing the architecture to a client-server model;
    • lack of condition;
    • caching;
    • uniformity of the interface;
    • layers;
    • code on demand (optional restriction).
  4. We examined the advantages that REST provides

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

  6. Let's take a closer look at the requests and responses. Their components were disassembled.

  7. Finally, we moved on to practice and wrote our own small RESTful application in Spring Boot. And we 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 the following:
  1. Following the description above, create your own Spring Boot project and implement in it the same logic as in the lecture. Repeat everything 1 in 1.
  2. Launch it. 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 described 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