Статьи

Spring REST: обработка исключений, вып.

Содержание

Привет всем, пришло время продолжать публиковать новые статьи в моем блоге. Поэтому я рад объявить, что планирую написать пару технических статей. В этом посте я собираюсь начать говорить об обработке исключений Spring REST. Spring предлагает нам несколько способов обработки исключений REST, но я хочу сосредоточить ваше внимание на двух из них: Весна-обработки исключений

  • @ExceptionHandler на уровне @Controller
  • @ExceptionHandler на уровне @ControllerAdvice

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

Итак, после краткого вступления я хочу подвести итог. Мы рассмотрим три примера обработчиков исключений REST. Каждый из этих трех случаев будет описывать решение некой реальной ситуации, которая может возникнуть в любом проекте Все разработки будут выполнены поверх уже существующего приложения .

подготовка

Первое, что я хочу сделать, — это добавить MessageSource в приложение. Это не очень сложно, и я не хочу останавливаться на этом подробно, потому что я объяснил, как это сделать в отдельном посте . Цель MessageSource — хранить сообщения об ошибках, которые я хочу вернуть клиенту в случае возникновения исключения.

Итак, вот файл messages.properties:

1
error.bad.smartphone.id = Smartphone can't have id:

После успешного добавления MessageSource мы можем продолжить обработку исключений на уровне @Controller .

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

В этом параграфе я хочу выделить фрагменты кода, где может возникнуть исключение. Давайте рассмотрим некоторые методы из SmartphoneController .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
    @RequestMapping(value="/edit/{id}", method=RequestMethod.GET)
    public ModelAndView editSmartphonePage(@PathVariable int id) {
        ModelAndView mav = new ModelAndView("phones/edit-phone");
        Smartphone smartphone = smartphoneService.get(id);
        mav.addObject("sPhone", smartphone);
        return mav;
    }
...
    @RequestMapping(value="/edit/{id}", method=RequestMethod.PUT,
            produces = MediaType.APPLICATION_JSON_VALUE,
            consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Smartphone editSmartphone(@PathVariable int id,
            @Valid @RequestBody Smartphone smartphone) {
        smartphone.setId(id);
        return smartphoneService.update(smartphone);
    }
...
    @RequestMapping(value="/delete/{id}", method=RequestMethod.DELETE,
            produces = MediaType.APPLICATION_JSON_VALUE,
            consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Smartphone deleteSmartphone(@PathVariable int id) {
        return smartphoneService.delete(id);
    }
...

Эти три метода имеют одну общую особенность — @PathVariable int id . Это обстоятельство важно, поскольку в документации Spring сказано, что если аргумент метода, аннотированный @PathVariable, не может быть приведен к указанному типу (в нашем случае к int), он будет представлен как String. Следовательно, это может вызвать исключение TypeMismatchException .

Для этого я буду использовать аннотацию @ExceptionHandler на уровне @Controller . Такой подход подходит для такой ситуации, как никто другой. Мне просто нужно сделать 2 изменения в SmartphoneController :

  • Добавить поле MessageSource
  • Добавить метод обработчика исключений
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
...
    @Autowired
    private MessageSource messageSource;
...
    @ExceptionHandler(TypeMismatchException.class)
    @ResponseStatus(value=HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorInfo handleTypeMismatchException(HttpServletRequest req, TypeMismatchException ex) {
        Locale locale = LocaleContextHolder.getLocale();
        String errorMessage = messageSource.getMessage("error.bad.smartphone.id", null, locale);
 
        errorMessage += ex.getValue();
        String errorURL = req.getRequestURL().toString();
 
        return new ErrorInfo(errorURL, errorMessage);
    }
...

Давайте рассмотрим метод. У аннотации @ExceptionHandler есть аргумент — TypeMismatchException , это означает, что метод будет срабатывать при возникновении исключения. Аннотация @ResponseStatus, используемая для указания конкретного кода состояния ответа.

Вы, наверное, заметили, что метод возвращает ErrorInfo. Здесь нет ничего сложного — это класс для любой ошибки, которая должна информировать клиента о причине ошибки. Итак, класс выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
public class ErrorInfo {
 
    private String url;
    private String message;
 
    public ErrorInfo(String url, String message) {
        this.url = url;
        this.message = message;
    }
 
//Getters and setters are omitted
 
}

Использование этого класса дает нам два основных преимущества: мы можем предоставить URL, который вызвал исключение, и мы можем предоставить соответствующее сообщение об ошибке.

Теперь давайте попробуем посмотреть, что мы имеем в случае, когда я пытаюсь получить доступ к некоторому URL с недопустимым идентификатором.

Весна-обработки исключений-контроллер

На скриншоте видно, что URL с неверным идентификатором был обработан так, как я указал на уровне @Controller . В следующей статье я расскажу о некоторых исключениях, которые мы можем разместить на уровне @ControllerAdvice .

Ссылка: Spring REST: Обработка исключений, вып. 1 от нашего партнера по JCG Алексея Зволинского в блоге Фрузенштейна .