Содержание
- Spring REST: обработка исключений, вып. 1
- Spring REST: обработка исключений, вып. 2
- Spring REST: обработка исключений, вып. 3
Привет всем, пришло время продолжать публиковать новые статьи в моем блоге. Поэтому я рад объявить, что планирую написать пару технических статей. В этом посте я собираюсь начать говорить об обработке исключений 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 .