Как настроить 404 ответ в контроллере Spring-MVC?

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

В Spring MVC для генерации ошибки 404 необходимо вызвать исключение ResponseStatusException с кодом статуса HttpStatus.NOT_FOUND:

Java
Скопировать код
@GetMapping("/somePath")
public void trigger404() {
    // Ваш вопрос: какой ваш любимый HTTP-статус? Ответ: Не найден – 404!
    throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Ваш ресурс везде искали, но так и не нашли");
}

Этот чёткий код показывает, что запрашиваемый ресурс не найден, что приводит к возникновению ошибки 404.

Рассмотрим также применение схожего подхода в контексте исключения ResourceNotFoundException.

Java
Скопировать код
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

@GetMapping("/resource/{id}")
public ResponseEntity<?> getResource(@PathVariable("id") String id) {
    // Вот вопрос: где этот ресурс?
    return yourResourceService.findById(id)
            .map(resource -> new ResponseEntity<>(resource, HttpStatus.OK))
            .orElseThrow(() -> new ResourceNotFoundException("Ответа нет. Это не игра в 'Найди Вальдо'."));
}

В приведённом примере при отсутствии ресурса генерируется ResourceNotFoundException, и благодаря аннотации @ResponseStatus сразу становится очевидным, что следует ожидать ошибку 404.

Кинга Идем в IT: пошаговый план для смены профессии

Детальное рассмотрение механизмов генерации ошибки 404

Подход с использованием ResponseEntity

Применяйте ResponseEntity для управления ответами с точностью джедая:

Java
Скопировать код
@GetMapping("/items/{id}")
public ResponseEntity<Object> getItem(@PathVariable Long id) {
    Item item = findItemById(id);
    // Отсутствует, как моя мотивация в понедельник
    if (item == null) {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
    return new ResponseEntity<>(item, HttpStatus.OK);
}

Этот подход предоставляет широкие возможности для тонкой настройки ответа, основываясь на вашей бизнес-логике.

Метод с использованием ModelAndView

Если вас привлекает идея отображения страниц (ведь мы все любим красивые вещи), ModelAndView станет вашим надёжным помощником:

Java
Скопировать код
@GetMapping("/viewItem/{id}")
public ModelAndView viewItem(@PathVariable Long id) {
    ModelAndView mav = new ModelAndView();
    Item item = findItemById(id);

    // Игра в прятки зашла слишком далеко
    if (item == null) {
        mav.setViewName("error/404");
        return mav;
    }

    mav.addObject("item", item);
    mav.setViewName("itemView");
    return mav;
}

Метод ModelAndView позволяет изящно управлять путём, по которому пользователи будут направлены на страницы ошибок.

Обработка исключений для REST-контроллеров

В RESTful API обработчики исключений — это как дежурные, поддерживающие порядок и чистоту вашего кода:

Java
Скопировать код
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }
}

Сочетание @RestControllerAdvice и @ExceptionHandler обеспечивает централизованное управление исключениями, поддерживая порядок и чистоту в структуре кода.

Инициализация ошибки 404 по условию и добавление гибкости

Гибкость функции isFound()

Иногда необходимо решить, следует ли обозначить ошибку 404, опираясь на конкретные условия:

Java
Скопировать код
@GetMapping("/profile")
public ResponseEntity<?> viewProfile(@RequestParam String username) {
    return userService.findUserByUsername(username)
            .map(user -> new ResponseEntity<>(user, HttpStatus.OK))
            .orElseGet(() -> {
                // Пользователь просто исчез!
                return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
            });
}

С помощью API Optional в Java 8 можно принимать решения, основываясь на методах isPresent или isFound, что делает код более лаконичным и надёжным.

Традиционный подход

В более ранних версиях Spring до 3.0.2 требуется более традиционный подход, напоминающий об использовании классического HttpServletResponse:

Java
Скопировать код
@GetMapping("/oldApproach/items/{id}")
public void getItem(@PathVariable Long id, HttpServletResponse response) {
    Item item = findItemById(id);
    // Если товар не найден, генерируем ошибку 404 – как город, покинутый жителями
    if (item == null) {
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        return;
    }
    // Иначе продолжаем обработку запроса о товаре
}

Несмотря на свою визуальную громоздкость, этот код предоставляет возможность ручного управления HTTP-статусами, напоминая о старых и простых методах решения задачи.

Проверка с использованием HttpServletRequest

В некоторых случаях полезно провести проверку с помощью HttpServletRequest, прежде чем сгенерировать ошибку 404:

Java
Скопировать код
@GetMapping("/checkRequest/items")
public ResponseEntity<Object> checkItem(HttpServletRequest request) {
    // Выполняем проверку, существует ли товар
    if (/* условие, основанное на запросе */) {
        // Товар пошёл в отпуск и не вернулся!
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
    // ...
}

Проверка с использованием HttpServletRequest позволит вам анализировать различные шаблоны URL и реагировать в зависимости от результата.

Визуализация

Представьте, что ваш контроллер в Spring-MVC — это парк аттракционов:

Markdown
Скопировать код
Добро пожаловать в 🎢 Парк Аттракционов (Ваш Контроллер)
  |
  🚶‍♂️Посетитель хочет покататься на аттракционе "🔎 ПоискАттракциона" (Точка Входа)
  |
  🎟️Проверка билета (Ваша Логика)
    |
    🚫 Нет такого аттракциона! Что делать?
    |
    👷‍♀️Сотрудник парка (Ваш Метод) берёт в руки дело...
    |
    🛑 **Генерирует исключение NoRideFoundException** – звонок на **404 Not Found**

Ваш сотрудник парка (исключение) всегда на защите, уверяя посетителей в том, что аттракцион недоступен, если он действительно таков.

Java
Скопировать код
@Controller
public class ThemeParkController {
    @RequestMapping("/findRide")
    public String findRide(@RequestParam("rideName") String rideName) {
        // Исчезнувший аттракцион, словно мои планы держать диету во время праздников
        if (!rideExists(rideName)) {
            throw new NoRideFoundException("Извините, аттракцион, который вы ищете, заявлен как закрытый на сегодня");
        }
        // Аттракцион найден! Добро пожаловать!
        return "rideView";
    }
}

Наш сотрудник парка всегда решительно действует, чтобы направить посетителей туда, куда нужно.

Полезные материалы

  1. Annotation Type ResponseStatus – Spring Framework — Узнайте, как отправлять определённые HTTP-статусы с помощью @ResponseStatus.
  2. Полное руководство по обработке исключений в Spring Boot — Практикуйте обработку исключений в приложениях Spring.
  3. Обработка исключения 404 Not Found в Spring MVC — Подчерпните полезные советы по работе с ошибками 404 в Spring MVC.
  4. Руководство по обработке ошибок Spring Boot для REST API — Освойте лучшие практики для эффективной обработки ошибок в RESTful-сервисах на Spring Boot.
  5. Документация Spring по обработке исключений — Погрузитесь в подробности работы @ExceptionHandler в официальной документации Spring.