Каким образом следует корректно обрабатывать ошибки в API методах?

Добрый день. <br/> Подскажите, пожалуйста, как можно переработать Контроллер и Сервис, чтобы при выполнении определённой проверки в Сервисе Контроллер возвращал конкретное сообщение об ошибке? Например: <br/> - если `inkRepo.selfCheck(id) != null`, то Контроллер должен извещать о невалидном ИД; <br/> - если `date_start.before(inkRepo.findDateEnd(id))`, то сообщать о неверной дате. <br/> <br/> В настоящее время все ошибки обрабатываются через общий null, что не кажется корректным... <br/> <br/> Контроллер: <br/> <pre><code class="java">@PatchMapping("changestart/{id}")
    public ResponseEntity&lt;Object&gt; changeDateStart(@PathVariable int id, @RequestBody InkDTO inkDTO) {
        Map&lt;String, Object&gt; response = inkService.changeDateStart(inktDTO.getDate_start(), id);
        if (response.get("id") == null) {
            Map&lt;String, String&gt; errorResponse = new HashMap&lt;&gt;();
            errorResponse.put("error", "ID не верный или дата начала &gt; даты окончания"); // Кастомное сообщение об ошибке
            return ResponseEntity.badRequest().body(errorResponse); // Возвращаем статус 400 с телом ответа
        } //если null в ответе, ID неверный
        return ResponseEntity.ok(response);
    }//изменить дату старта</code></pre> <br/> <br/> Сервис: <br/> <pre><code class="java">public Map changeDateStart(Date date_start, int id){
        Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();
        if (inkRepo.selfCheck(id) != null){
            if (date_start.before(inkRepo.findDateEnd(id))){
                long dura = myСompareDate.compare(date_start, inkRepo.findDateEnd(id)); //высчитываем: дата старта - дата окончания
                inktRepo.changeDateStart(date_start, dura, id);
                System.out.println(" успешно "); // продолжение кода </code></pre>
  • 1 февраля 2025 г. 15:34
Ответы на вопрос 1
Для улучшения обработки ошибок в ваших API методах, вам следует разработать более четкую и централизованную логику обработки ошибок. Это упростит код и сделает его более читаемым, а также обеспечит возврат конкретных и понятных сообщений об ошибках.

### Рекомендуемый подход:

1. **Создание пользовательских исключений.** Вы можете создать свои собственные классы исключений для каждой конкретной ошибки, например `InvalidIdException` и `InvalidDateException`.

2. **Обработка исключений в контроллере с помощью аннотации `@ExceptionHandler`.** Это позволит вам централизовать логику обработки ошибок.

3. **Возвращение детализированного ответа об ошибке через `ResponseEntity`.**

### Пример реализации:

**1. Создание пользовательских исключений:**

```java
public class InvalidIdException extends RuntimeException {
    public InvalidIdException(String message) {
        super(message);
    }
}

public class InvalidDateException extends RuntimeException {
    public InvalidDateException(String message) {
        super(message);
    }
}
```

**2. Изменение сервиса для выброса исключений:**

```java
public Map<String, Integer> changeDateStart(Date date_start, int id){
    Map<String, Integer> map = new HashMap<>();

    if (inkRepo.selfCheck(id) != null) {
        throw new InvalidIdException("ID не верный");
    }
    
    if (date_start.before(inkRepo.findDateEnd(id))) {
        throw new InvalidDateException("Дата начала не может быть раньше даты окончания");
    }

    long dura = myCompareDate.compare(date_start, inkRepo.findDateEnd(id));
    inkRepo.changeDateStart(date_start, dura, id);
    System.out.println(" успешно ");
    return map; // Заполняйте ответ в зависимости от логики
}
```

**3. Обработка исключений в контроллере:**

```java
@RestController
@RequestMapping("/api/inks")
public class InkController {

    @Autowired
    private InkService inkService;

    @PatchMapping("changestart/{id}")
    public ResponseEntity<Object> changeDateStart(@PathVariable int id, @RequestBody InkDTO inkDTO) {
        try {
            Map<String, Integer> response = inkService.changeDateStart(inkDTO.getDate_start(), id);
            return ResponseEntity.ok(response);
        } catch (InvalidIdException e) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("error", e.getMessage()));
        } catch (InvalidDateException e) {
            return ResponseEntity.badRequest().body(Collections.singletonMap("error", e.getMessage()));
        }
    }
}
```

**4. (Опционально) Создание контроллера для обработки ошибок:**

Вы можете создать отдельный контроллер, который будет обрабатывать исключения.

```java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(InvalidIdException.class)
    public ResponseEntity<Object> handleInvalidIdException(InvalidIdException e) {
        return ResponseEntity.badRequest().body(Collections.singletonMap("error", e.getMessage()));
    }

    @ExceptionHandler(InvalidDateException.class)
    public ResponseEntity<Object> handleInvalidDateException(InvalidDateException e) {
        return ResponseEntity.badRequest().body(Collections.singletonMap("error", e.getMessage()));
    }
}
```

### Заключение
С помощью описанных шагов ваш код станет более организованным и понятным. Теперь контроллер будет возвращать четкие сообщения об ошибках, а обработка исключений будет централизована и не будет загрязнять основной бизнес-логикой.
Похожие вопросы