ЗМІСТ ЦИКЛУ СТАТТІВ І знову привіт! Настав час струсити пил з клавіатури. Створюємо spring-boot проект. Із залежностей мавену нам потрібні:
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Перш ніж читати далі, створіть структуру проекту: BusinessException та CustomException:
public class BusinessException extends Exception{
public BusinessException(String message) {
super(message);
}
}
public class CustomException extends Exception{
public CustomException(String message) {
super(message);
}
}
та клас Response
public class Response {
private String message;
public Response() {
}
public Response(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
А тепер, я зроблю фінт вухами, і передам слово Олексію Кутепову, у своїй статті Обробка винятків у контролерах Spring він розповість нам, як ці файли наповнити правильним вмістом. Читайте не кваплячись, усі приклади акуратно переписуєте до себе в проект, запускайте та тестуйте у постмані. Якщо у статті Олексія, у вас викликала питання наступний рядок: produces = APPLICATION_JSON_VALUE , то знайте, що до обробки винятків вона не має відношення, вона каже, що за замовчуванням всі методи цього контролера будуть віддавати JSON. При необхідності в конкретному методі це значення можна перевизначити на інший MediaType. Якщо ви прочитали, йдемо далі. У запропонованій вище статті розглянуто різні варіанти обробників. Найбільш гнучкий з них: @ControllerAdvice – він дозволяє змінити як код, так і тіло стандартної відповіді за помилки. Крім того, він дозволяє в одному методі опрацювати відразу кілька винятків. Але це ще не все, якщо ви прочитаєте далі, то отримаєте покращений @ControllerAdvice абсолютно безкоштовно. Проведемо підготовчі роботи: хочу щоб у відповіді виводабося як кастомне так і стандартне повідомлення про помилку. Для цього внесемо зміну до класу Response : додамо ще одне поле
private String debugMessage;
Створимо додатковий конструктор:
public Response(String message, String debugMessage) {
this.message = message;
this.debugMessage = debugMessage;
}
і не забудьте створити Getter і Setter для нового поля. Тепер до діла. Напишемо ще один контролер:
@RestController
public class Example7Controller {
@GetMapping(value = "/testExtendsControllerAdvice")
public ResponseEntity<?> testExtendsControllerAdvice(@RequestBody Response response) {
return ResponseEntity.ok(response);
}
}
Протестуємо у постман: На http://localhost:8080/testExtendsControllerAdvice відправимо JSON
{
"message": "message"
}
У відповідь отримаємо статус 200 і тіло
{
"message": "message",
"debugMessage": null
}
Тепер пошлемо свідомо неправильний JSON
{
11"message": "message"
}
У відповідь отримаємо статус 400 (якщо забули, що він означає, гляньте в інтернеті) та порожнє тіло відповіді. Звичайно, нікого це не влаштовує, давайте з цим боротися. Раніше ми створювали @ControllerAdvice з нуля, але в Spring Boot існує заготовка - ResponseEntityExceptionHandler . У ній вже опрацьовано багато винятків, наприклад: NoHandlerFoundException , HttpMessageNotReadableException , MethodArgumentNotValidException та інші. Цей клас займається обробкою помилок. Він має купу методів, назва яких побудована за принципом handle + назва виключення. Якщо ми хочемо обробити якийсь базовий виняток, то успадковуємось від цього класу та перевизначаємо потрібний метод. Доопрацюємо клас дефолтного едвайсу
@ControllerAdvice
public class DefaultAdvice extends ResponseEntityExceptionHandler {//унаследовались от обработчика-заготовки
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Response> handleException(BusinessException e) {
Response response = new Response(e.getMessage());
return new ResponseEntity<>(response, HttpStatus.OK);
}
//Небольшое отступление: В обработчике выше, обратите внимание на HttpStatus.OK,
//он может быть и HttpStatus.BAD_REQUEST або другим, тут ограничений нет,
//попробуйте поменять статусы и потестить этот обработчик
@Override//переопределабо метод родительского класса
protected ResponseEntity<Object> handleHttpMessageNotReadable
(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Response response = new Response("Не правильный JSON",ex.getMessage());
return new ResponseEntity<>(response, status);
}
}
Як ви помітабо, був перевизначений обробник, який відповідає за HttpMessageNotReadableException . Цей виняток виникає тоді, коли тіло запиту, що приходить у метод контролера, нечитане – наприклад, некоректний JSON. За цей виняток відповідає метод handleHttpMessageNotReadable () . Ще раз зробимо запит з некоректним JSON: http://localhost:8080/testExtendsControllerAdvice
{
11"message": "message"
}
Отримуємо відповідь з кодом 400 (Bad Request) та тілом:
{
"message": "Не правильный JSON",
"debugMessage": "JSON parse error: Unexpected character ('1' (code 49)): was expecting double-quote to start field name; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('1' (code 49)): was expecting double-quote to start field name\n at [Source: (PushbackInputStream); line: 2, column: 6]"
}
Тепер відповідь містить як коректний код, а й тіло з інформативними повідомленнями. Перевіримо як працює з коректним JSON Запит:
{
"message": "message"
}
Отримали відповідь:
{
"message": "message",
"debugMessage": null
}
Якщо чесно, мені не подобається що у відповіді є поле зі значенням null , зараз швиденько це виправимо. Переходимо в клас Response і ставимо інструкцію над потрібним полем
@JsonInclude(JsonInclude.Include.NON_NULL)
private String debugMessage;
Перезапускаємо проект, робимо ще раз попередній запит, у відповіді отримуємо:
{
"message": "message"
}
Завдяки анотації @JsonInclude(JsonInclude.Include.NON_NULL) це поле буде включено у відповідь тільки в тому випадку, якщо ми його поставимо. @JsonInclude входить до бібліотеки анотацій Jackson , дуже корисно знати, що вона може. Ось дві статті на вибір: Джексон анотації. Автор перекладав, але не доперекладав , гугл трансліт відмінно впоратися. Валідація Потрібно доповнити цю тему таким поняттям як валідація. Просто кажучи, це перевірка що об'єкт є той об'єкт що ми очікуємо. Наприклад: якщо ми в додатку "Телефонний довідник" маємо перевіряти наявність телефонних номерів у БД, то перш ніж лізти в базу, логічно перевіряти, а чи не ввів користувач замість цифр літери. Три статті з валідації, зростання складності: Валідація бінів у Spring Налаштування валідації DTO у Spring Framework Валідація даних у Spring Boot З теорією, на сьогодні закінчабо. Для тренування пропоную наступне завдання: Необхідно реалізувати програму NightclubBouncer (Вибивала нічного клубу). Вимоги: 1) Програма повинна приймати на вхід JSON і робити запис до бази даних. Приклад JSON:
{
"name": "Katy Perry"
“status”: “super star”
}
І в тілі відповіді має бути напис: Welcome + name ! 2) У додатку повинні бути реалізовані методи: - Виведення запису по id з БД в клієнт (Postman). - Видалення запису по полю: name . 3) Має бути реалізований мапінг із шару dto в entity і назад. 4) Додаток повинен викидати помилку KickInTheAssException (її потрібно розробити самим) якщо поле status у вході JSON не дорівнює: super star 5) Помилка KickInTheAssException повинна оброблятися ControllerAdvice, і в тілі відповіді має бути повідомлення: «Don't let me see you here again!». Статус відповіді має бути 400. 6) Стандартна помилка EntityNotFoundException , що виникає, наприклад, якщо до клубу зайшла тільки Кеті Пері та збереглася в базу з id = 1 , а ви викликали метод «виведення запису з id» і захотіли вивести запис з id = 2 , Якої немає в базі. Цю помилку необхідно обробити перевизначеним методом класу ResponseEntityExceptionHandler, яким саме - розібратися вам самим. Відповідь має мати відповідний статус. 7) Зробіть валідацію: простий варіант - поля JSON повинні бути не null, складніше поле "name" має складатися з двох слів латинського алфавіту і вони обидва повинні починатися з великої літери. Невалідні значення повинні викликати виняток, обробіть його будь-яким способом, виведіть відповідний код помилки та повідомлення про помилку: No validate. І реалізуйте це все без використання бібліотеки Lombok, не включайте її залежно від проекту 😅
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ