Це остання частина аналізу REST. У попередніх частинах:
Створення проекту
У даному розділі ми створимо невеликий RESTful додаток на Spring Boot. У нашому додатку буде реалізовано CRUD (Create, Read, Update, Delete) операції над клієнтами з прикладу з попередньої частини розбору. Для початку створимо новий Spring Boot додаток через меню File -> New -> Project... У вікні, що відкрилося, вибираємо Spring Initializr і вказуємо Project SDK: Натискаємо кнопку Next. У наступному вікні вказуємо тип проекту Maven, вказуємо Group та Artifact: Натискаємо кнопку Next. У наступному вікні необхідно вибрати потрібні для проекту компоненти Spring Framework. Нам буде достатньо Spring Web: Натискаємо кнопку Next. Далі залишилося вказати лише найменування проекту та його розташування у файловій системі: Натискаємо кнопку Finish. Проект створений, тепер ми можемо побачити його структуру: IDEA згенерувала за нас дескриптор розгортання системи складання Maven - pom.xml та головний клас програми:RestExampleApplication
. Наведемо їх код:
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);
}
}
Створення REST функціоналу
Наш додаток керує клієнтами. Тому насамперед нам необхідно створити сутність клієнта. Це буде клас POJO. Створимо пакетmodel
усередині пакета com.codegym.lectures.rest_example
. Усередині пакету model
створимо клас 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;
}
}
У сервісі буде реалізовано CRUD операції над клієнтом. Наступним кроком ми маємо створити сервіс, у якому будуть реалізовані ці операції. У пакеті com.codegym.lectures.rest_example
створимо пакет service
, усередині якого створимо інтерфейс ClientService
. Наведемо код інтерфейсу з коментарями:
public interface ClientService {
/**
* Создает нового клиента
* @param client - клиент для создания
*/
void create(Client client);
/**
* Повертає список всех имеющихся клиентов
* @return список клиентов
*/
List<client> readAll();
/**
* Повертає клиента по его ID
* @param id - ID клиента
* @return - об'єкт клиента с заданным 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);
}
Далі нам потрібно створити реалізацію цього інтерфейсу. Зараз у ролі сховища клієнтів виступатиме Map<Integer, Client>
. Ключем картки буде ID клієнта, а значенням - сам клієнт. Зроблено це у тому, ніж перевантажувати приклад специфікою роботи з БД. Однак, у майбутньому ми зможемо написати іншу реалізацію інтерфейсу, в якій можна буде підключити реальну базу даних. У пакеті service
створимо реалізацію інтерфейсу ClientService
:
@Service
public class ClientServiceImpl implements ClientService {
// Хранабоще клиентов
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;
}
}
Анотація @Service
каже спрингу, що цей клас є сервісом. Це спеціальний тип класів, у якому реалізується деяка бізнес-логіка програми. Згодом, завдяки цій інструкції Spring надаватиме нам екземпляр даного класу в місцях, де це потрібно за допомогою Dependency Injection. Тепер настав час для створення контролера. Спеціального класу, де ми реалізуємо логіку обробки клієнтських запитів на ендпоінти (URI). Щоб було зрозуміліше, створюватимемо цей клас частинами. Спочатку створимо сам клас і впровадимо в нього залежність від ClientService
:
@RestController
public class ClientController {
private final ClientService clientService;
@Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
}
Пояснимо анотації: @RestController - каже спрингу, що цей клас є REST контролером. Тобто. в даному класі буде реалізована логіка обробки клієнтських запитів @Autowired - каже спрингу, що тут необхідно запровадити залежність. У конструктор ми передаємо інтерфейс ClientService
. Реалізацію цього сервісу ми позначабо інструкцією @Service
раніше, і тепер спринг зможе передати екземпляр цієї реалізації в конструктор контролера. Далі ми покроково реалізовуватимемо кожен метод контролера, для обробки CRUD операцій. Почнемо з операції Create. Для цього напишемо метод create
:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
clientService.create(client);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Розберемо даний метод: @PostMapping(value = "/clients")
- Тут ми позначаємо, що даний метод обробляє запити POST на адресау /clients Метод повертає ResponseEntity<?>
. ResponseEntity
- Спеціальний клас для повернення відповідей. За допомогою нього ми зможемо надалі повернути клієнту HTTP статус код. Метод приймає параметр @RequestBody Client client
, значення цього параметра підставляється із тіла запиту. Про це свідчить інструкція @RequestBody
. Усередині тіла методу ми викликаємо метод create у раніше створеного сервісу та передаємо йому прийнятого у параметрах контролера клієнта. Після цього повертаємо статус 201 Created, створивши новий об'єкт ResponseEntity
і передавши в нього необхідне значення енума HttpStatus
. Далі реалізуємо операцію Read
: Для початку реалізуємо операцію отримання списку всіх наявних клієнтів:
@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")
— все аналогічно до анотації @PostMapping
, тільки тепер ми обробляємо GET запити. Цього разу ми повертаємо ResponseEntity<List<Client>>
тільки цього разу, крім HTTP статусу, ми повернемо ще й тіло відповіді, яким буде список клієнтів. У REST контролерах спрингу всі POJO об'єкти, а також колекції POJO об'єктів, які повертаються як тіло відповідей, автоматично серіалізуються в JSON, якщо явно не вказано інше. Нас це цілком влаштовує. Всередині методу за допомогою нашого сервісу ми отримуємо список усіх клієнтів. Далі, якщо список не null і не порожній, ми повертаємо за допомогою класу ResponseEntity
сам список клієнтів і HTTP статус 200 OK. Інакше ми повертаємо просто статус HTTP 404 Not Found. Далі реалізуємо можливість отримувати клієнта за його 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);
}
З нового, у нас тут з'явилася змінна дорога. Змінна, визначена в URI. value = "/clients/{id}"
. Ми вказали її у фігурних дужках. А в параметрах методу приймаємо її як змінну int
, за допомогою анотації @PathVariable(name = "id")
. Даний метод прийматиме запити на uri виду /clients/{id}
, де замість {id}
може бути будь-яке чисельне значення. Дане значення, згодом, передається змінною int id
- параметр методу. У тілі ми отримуємо об'єкт Client
за допомогою нашого сервісу та прийнятого id
. І далі, за аналогією зі списком, повертаємо статус 200 OK і сам об'єкт Client
, або просто статус 404 Not Found, якщо клієнта з таким id
не виявилося в системі. Залишилося реалізувати дві операції - Update та Delete. Наведемо код цих методів:
@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);
}
Чогось суттєво нового в даних методах немає, тому докладний опис пропустимо. Єдине, що варто сказати: метод update
обробляє PUT запити (анотація @PutMapping
), а метод delete
обробляє DELETE запити (анотація DeleteMapping
). Наведемо повний код контролера:
@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);
}
}
У результаті структура нашого проекту виглядає так:
Запуск та тестування
Щоб запустити нашу програму, достатньо запустити методmain
у класі RestExampleApplication
. А для того, щоб тестувати RESTful веб сервіси, потрібно завантажити нове ПЗ) Справа в тому, що GET запити досить просто відправляти зі звичайного браузера, а ось для POST, PUT і DELETE звичайним браузером не обійтися. Не хвилюйся: щоб надсилати будь-які HTTP запити, можна скористатися програмою Postman. Завантажити її можна звідси . Після скачування та встановлення, приступаємо до тестування нашої програми. Для цього відкриваємо програму та створюємо новий запит: Натискаємо кнопку New у лівому верхньому кутку. Далі вибираємо Request: Далі задаємо йому ім'я та зберігаємо його. Спробуємо тепер відправити запит на сервер POST і створити першого клієнта: Створюємо в такий спосіб кілька клієнтів. Потім змінюємо тип запиту на GET і відправляємо його на сервер:
Загальні підсумки
Вітаю: ми розглянули досить тему REST. Весь матеріал вийшов об'ємним, але, сподіваємось, корисним для тебе:-
Ми довідалися, що таке REST.
-
Ознайомабося з історією виникнення REST.
-
Поговорабо про обмеження та принципи даного архітектурного стилю:
- приведення архітектури до моделі клієнт-сервер;
- відсутність стану;
- кешування;
- однаковість інтерфейсу;
- шари;
- код на вимогу (необов'язкове обмеження).
-
Розібрали переваги, які дає REST
-
Детально розглянули, як сервер та клієнт взаємодіють один з одним за протоколом HTTP.
-
Ближче познайомабося із запитами та відповідями. Розібрали їхні складові.
-
Нарешті, ми перейшли до практики та написали свій невеликий RESTful додаток на Spring Boot. І навіть навчабося тестувати його за допомогою програми Postman.
Домашнє завдання
Спробуй зробити таке:- Дотримуючись опису вище, створи самостійно Spring Boot проект і реалізуй у ньому ту ж логіку, що й у лекції. Повтори всі 1 у 1.
- Запусти. додаток.
- Скачай та налаштування Postman (або будь-який інший інструмент для відправки запитів, хоч curl).
- Протестуй запити POST та GET так само, як було зазначено у лекції.
- Протестуй запити PUT та DELETE самостійно.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ