Рубрики
Без рубрики

Пример обработки исключений Spring Boot

В этом руководстве мы рассмотрим пример обработки исключений Spring Boot, который использует… С тегами spring, spring boot, java, webdev.

В этом руководстве мы рассмотрим пример обработки исключений Spring Boot, в котором используется @ControllerAdvice и @ExceptionHandler .

Вы также можете обработать исключение Restful API с помощью @RestControllerAdvice , пожалуйста, посетите: пример @RestControllerAdvice в Spring Boot

Эта статья первоначально опубликована по адресу Bezkoder .

Обработка исключений Rest API

Мы создали контроллер Rest для операций CRUD и метода finder. Давайте посмотрим на код: (пошаговая сборка Rest API находится в:

@RestController
public class TutorialController {

  @Autowired
  TutorialRepository tutorialRepository;

  @GetMapping("/tutorials")
  public ResponseEntity> getAllTutorials(@RequestParam(required = false) String title) {
    try {
      ...
      return new ResponseEntity<>(tutorials, HttpStatus.OK);
    } catch (Exception e) {
      return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @GetMapping("/tutorials/{id}")
  public ResponseEntity getTutorialById(@PathVariable("id") long id) {
    Optional tutorialData = tutorialRepository.findById(id);

    if (tutorialData.isPresent()) {
      return new ResponseEntity<>(tutorialData.get(), HttpStatus.OK);
    } else {
      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
  }

  @PutMapping("/tutorials/{id}")
  public ResponseEntity updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
    Optional tutorialData = tutorialRepository.findById(id);

    if (tutorialData.isPresent()) {
      ...
      return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
    } else {
      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
  }

  ...

  @DeleteMapping("/tutorials/{id}")
  public ResponseEntity deleteTutorial(@PathVariable("id") long id) {
    try {
      tutorialRepository.deleteById(id);
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    } catch (Exception e) {
      return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @DeleteMapping("/tutorials")
  public ResponseEntity deleteAllTutorials() {
    // try and catch
  }

  @GetMapping("/tutorials/published")
  public ResponseEntity> findByPublished() {
    // try and catch
  }
}

Вы можете видеть, что мы используем try/catch много раз для аналогичного исключения ( INTERNAL_SERVER_ERROR ), и также есть много случаев, которые возвращают NOT_FOUND .

Есть ли в любом случае, чтобы сделать их простыми, какой-либо способ быстро и гибко прикрепить сообщение об ошибке с ответом? Давайте решим эту проблему сейчас.

Обработка исключений с помощью рекомендаций контроллера весной

Spring поддерживает обработку исключений с помощью глобального обработчика исключений ( @ExceptionHandler ) с рекомендациями контроллера ( @ControllerAdvice ). Это позволяет использовать механизм, который делает ResponseEntity работа с безопасностью типов и гибкостью @ExceptionHandler :

@ControllerAdvice
public class ControllerExceptionHandler {

  @ExceptionHandler(value = {ResourceNotFoundException.class, CertainException.class})
  public ResponseEntity resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ErrorMessage message = new ErrorMessage(
        status,
        date,
        ex.getMessage(),
        description);

    return new ResponseEntity(message, HttpStatus.NOT_FOUND);
  }
}

Аннотация @ControllerAdvice является специализацией аннотации @Component , поэтому она автоматически определяется с помощью сканирования пути к классу. ControllerAdvice – это своего рода перехватчик, который окружает логику наших контроллеров и позволяет нам применять к ним некоторую общую логику.

Его методы (с аннотацией @ExceptionHandler ) совместно используются глобально несколькими компонентами @Controller для захвата исключений и преобразования их в HTTP-ответы. Аннотация @ExceptionHandler указывает, какой тип исключения мы хотим обработать. Экземпляр exception и request будут введены с помощью аргументов метода.

Используя две аннотации вместе, мы можем:

  • управляйте телом ответа вместе с кодом состояния
  • обрабатывать несколько исключений одним и тем же методом

@ResponseStatus Статус ответа

В приведенном выше примере мы используем @ControllerAdvice для веб-служб REST и возвращаем ResponseEntity объект дополнительно.

Spring также предоставляет аннотацию @ResponseBody , которая сообщает контроллеру, что возвращаемый объект автоматически сериализуется в JSON и передается объекту HttpResponse . Этот способ не требует ResponseEntity но вам нужно использовать @ResponseStatus , чтобы установить код состояния HTTP для этого исключения.

@ControllerAdvice
@ResponseBody
public class ControllerExceptionHandler {

  @ExceptionHandler(ResourceNotFoundException.class)
  @ResponseStatus(value = HttpStatus.NOT_FOUND)
  public ErrorMessage resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ErrorMessage message = new ErrorMessage(...);
    return message;
  }
}

Настройка проекта Весенней загрузки

Вы можете следовать шаг за шагом или получить исходный код в одном из следующих постов:

Проект Spring содержит структуру, в которую нам нужно только добавить некоторые изменения, чтобы разбивка на страницы работала хорошо.

Или вы можете получить новый исходный код Github в конце этого руководства.

Окончательная структура проекта будет выглядеть следующим образом:

Определить Сообщение Об Ошибке В Ответе

Мы хотим создать нашу собственную структуру ответа на сообщение вместо использования ответа на ошибку по умолчанию, предоставляемого Spring Boot. Давайте определим конкретную структуру ответов на ошибки.

исключение / ErrorMessage.java

package com.bezkoder.spring.exhandling.exception;

import java.util.Date;

public class ErrorMessage {
  private int statusCode;
  private Date timestamp;
  private String message;
  private String description;

  public ErrorMessage(int statusCode, Date timestamp, String message, String description) {
    this.statusCode = statusCode;
    this.timestamp = timestamp;
    this.message = message;
    this.description = description;
  }

  public int getStatusCode() {
    return statusCode;
  }

  public Date getTimestamp() {
    return timestamp;
  }

  public String getMessage() {
    return message;
  }

  public String getDescription() {
    return description;
  }
}

Создать пользовательское исключение

Мы собираемся создать исключение для ресурса, не найденного в контроллере Spring Boot. Давайте создадим ResourceNotFoundException класс, который расширяет Исключение RuntimeException .

исключение / исключение

package com.bezkoder.spring.exhandling.exception;

public class ResourceNotFoundException extends RuntimeException {

  private static final long serialVersionUID = 1L;

  public ResourceNotFoundException(String msg) {
    super(msg);
  }
}

Создайте совет контроллера с помощью @ExceptionHandler

Теперь мы собираемся создать специальный класс, который аннотируется @ControllerAdvice аннотацией. Этот класс обрабатывает конкретное исключение ( Resourcenotfoundexception ) и глобальное исключение только в одном месте.

исключение / исключение

package com.bezkoder.spring.exhandling.exception;

import java.util.Date;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

import com.bezkoder.spring.exhandling.exception.ErrorMessage;
import com.bezkoder.spring.exhandling.exception.ResourceNotFoundException;

@ControllerAdvice
public class ControllerExceptionHandler {

  @ExceptionHandler(ResourceNotFoundException.class)
  public ResponseEntity resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ErrorMessage message = new ErrorMessage(
        HttpStatus.NOT_FOUND.value(),
        new Date(),
        ex.getMessage(),
        request.getDescription(false));

    return new ResponseEntity(message, HttpStatus.NOT_FOUND);
  }

  @ExceptionHandler(Exception.class)
  public ResponseEntity globalExceptionHandler(Exception ex, WebRequest request) {
    ErrorMessage message = new ErrorMessage(
        HttpStatus.INTERNAL_SERVER_ERROR.value(),
        new Date(),
        ex.getMessage(),
        request.getDescription(false));

    return new ResponseEntity(message, HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

Измените контроллер для использования @ControllerAdvice

У вашего контроллера Rest теперь нет блока try/catch, и он выдаст ResourceNotFoundException , куда мы хотим отправить NOT_FOUND notification в ответном сообщении.

контроллер / контроллер

package com.bezkoder.spring.exhandling.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.bezkoder.spring.exhandling.exception.ResourceNotFoundException;
import com.bezkoder.spring.exhandling.model.Tutorial;
import com.bezkoder.spring.exhandling.repository.TutorialRepository;

@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {

  @Autowired
  TutorialRepository tutorialRepository;

  @GetMapping("/tutorials")
  public ResponseEntity> getAllTutorials(@RequestParam(required = false) String title) {
    List tutorials = new ArrayList();

    if (title == null)
      tutorialRepository.findAll().forEach(tutorials::add);
    else
      tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);

    if (tutorials.isEmpty()) {
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    return new ResponseEntity<>(tutorials, HttpStatus.OK);
  }

  @GetMapping("/tutorials/{id}")
  public ResponseEntity getTutorialById(@PathVariable("id") long id) {
    Tutorial tutorial = tutorialRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("Not found Tutorial with id = " + id));

    return new ResponseEntity<>(tutorial, HttpStatus.OK);
  }

  @PostMapping("/tutorials")
  public ResponseEntity createTutorial(@RequestBody Tutorial tutorial) {
    Tutorial _tutorial = tutorialRepository.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
    return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
  }

  @PutMapping("/tutorials/{id}")
  public ResponseEntity updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
    Tutorial _tutorial = tutorialRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("Not found Tutorial with id = " + id));

    _tutorial.setTitle(tutorial.getTitle());
    _tutorial.setDescription(tutorial.getDescription());
    _tutorial.setPublished(tutorial.isPublished());

    return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
  }

  @DeleteMapping("/tutorials/{id}")
  public ResponseEntity deleteTutorial(@PathVariable("id") long id) {
    tutorialRepository.deleteById(id);

    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }

  @DeleteMapping("/tutorials")
  public ResponseEntity deleteAllTutorials() {
    tutorialRepository.deleteAll();

    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }

  @GetMapping("/tutorials/published")
  public ResponseEntity> findByPublished() {
    List tutorials = tutorialRepository.findByPublished(true);

    if (tutorials.isEmpty()) {
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    return new ResponseEntity<>(tutorials, HttpStatus.OK);
  }

}

Запуск и тестирование

Мы завершаем внедрение CRUD REST API и обработку исключений для него. Запустите приложение Spring Boot с помощью команды: mvn spring-boot:run .

  • Получите несуществующий учебник:
  • Обновите несуществующий учебник:
  • Создать учебное пособие с неправильными полями:
  • Удалить несуществующий учебник:

Вывод

Сегодня мы создали класс обработки исключений для Spring Boot Rest API, используя @ControllerAdvice и @ExceptionHandler . Теперь вы можете легко создать свой собственный пользовательский класс обработчика исключений или обрабатывать глобальное исключение в одном месте.

Если вы хотите добавить разбивку на страницы в этот проект Spring, вы можете найти инструкцию по адресу: Пример разбивки на страницы и фильтрации Spring Boot | Spring JPA, Доступный для просмотра по страницам

Для сортировки/упорядочения по нескольким полям: Spring Data JPA Сортировка/Упорядочение по нескольким столбцам | Spring Boot

Или способ написания модульного теста для репозитория JPA: Модульный тест Spring Boot для репозитория JPA с помощью @DataJpaTest

Вы также можете узнать, как развернуть это приложение Spring Boot на AWS (бесплатно) с помощью этого руководства . Или Dockerize с помощью Docker Compose: Пример Spring Boot и MySQL

Счастливого обучения! Еще увидимся.

Дальнейшее Чтение

Связанные Сообщения:

Больше Практики:

исходный код

Вы можете найти полный исходный код этого руководства по адресу Github .

Оригинал: “https://dev.to/tienbku/spring-boot-exception-handling-example-241c”