1. Введение
Возможно, мы сталкивались с некоторыми из нескольких способов обработки исключений в приложении веб-службы RESTful в Spring. В этой статье мы попытаемся изучить лучший подход, который мы можем использовать для достижения эффективной обработки исключений.
2. Постановка проблемы
Давайте создадим простое приложение, которое будет идентифицировать имя сотрудника в REST URI. Если имя сотрудника, указанное в запросе, является числовым, пусть наше приложение сгенерирует специальное исключение, которое мы будем обрабатывать с помощью обработчиков исключений , и, соответственно, вернем ответ JSON клиенту. Ответом об успешном выполнении будет JSON с подробностями сотрудника, а ответом об ошибке будет JSON с ошибкой с errorCode и соответствующим сообщением об ошибке.
3. Реализация
Давайте сначала проверим, как выглядят записи нашего файла pom и web.xml —
pom.xml
|
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
|
<!-- Spring dependencies --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.1.RELEASE</version></dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.2.1.RELEASE</version></dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.2.1.RELEASE</version></dependency> <!-- Jackson JSON Processor --><dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.4.1</version></dependency> |
web.xml
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
<?xml version="1.0" encoding="ISO-8859-1"?> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee id="WebApp_ID" version="2.5"> <display-name>RESTWithSpringMVCException</display-name> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> |
Давайте теперь проверим контекст веб-приложения .
MVC-диспетчерская-servlet.xml
|
01
02
03
04
05
06
07
08
09
10
11
12
|
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <mvc:annotation-driven /> <context:component-scan base-package="com.jcombat.controller" /> </beans> |
Теперь пришло время создать классы сущностей, один для Employee, а другой для ErrorResponse , который должен быть возвращен как JSON в случае любого исключения в любом из слоев в нашем приложении.
Employee.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package com.jcombat.bean; public class Employee { private String empId; private String name; public String getEmpId() { return empId; } public void setEmpId(String empId) { this.empId = empId; } public String getName() { return name; } public void setName(String name) { this.name = name; }} |
ErrorResponse.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package com.jcombat.bean; public class ErrorResponse { private int errorCode; private String message; public int getErrorCode() { return errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; }} |
Нам также нужен собственный класс исключений. Я надеюсь, что мы все уже знаем о пользовательских исключениях. Давайте быстро создадим один для нашего приложения.
EmployeeException.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
package com.jcombat.exception; public class EmployeeException extends Exception { private static final long serialVersionUID = 1L; private String errorMessage; public String getErrorMessage() { return errorMessage; } public EmployeeException(String errorMessage) { super(errorMessage); this.errorMessage = errorMessage; } public EmployeeException() { super(); }} |
Spring предоставляет нам аннотацию @ExceptionHandler для специальной обработки определенного или распространенного типа исключений в контроллере.
Наиболее важной частью здесь является написание контроллера покоя для нашего приложения.
DemoController.java
|
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package com.jcombat.controller; import org.apache.commons.lang3.StringUtils;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController; import com.jcombat.bean.Employee;import com.jcombat.bean.ErrorResponse;import com.jcombat.exception.EmployeeException; @RestControllerpublic class EmployeeController { @RequestMapping(value = "/{firstName}", method = RequestMethod.GET) public ResponseEntity<Employee> showMessage( @PathVariable("firstName") String firstName, @RequestParam(value = "empId", required = false, defaultValue = "00000") final String empId) throws EmployeeException { Employee employee = new Employee(); employee.setEmpId(empId); employee.setFirstName(firstName); if (StringUtils.isNumeric(firstName)) { throw new EmployeeException("Invalid employee name requested"); } return new ResponseEntity<Employee>(employee, HttpStatus.OK); } @ExceptionHandler(EmployeeException.class) public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex) { ErrorResponse error = new ErrorResponse(); error.setErrorCode(HttpStatus.PRECONDITION_FAILED.value()); error.setMessage(ex.getMessage()); return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK); }} |
Обратите внимание на метод @ExceptionHandler в нашем контроллере, который должен обрабатывать только исключение EmployeeException, выброшенное на любом из уровней нашего приложения.
Но что, если NullPointerException генерируется из ниоткуда. Чтобы быть в безопасности, у нас должен быть общий обработчик исключений в нашем приложении, который обрабатывает все другие типы исключений, такие как IOException , NullPointerException и так далее. Для этого Spring представил версию @ControllerAdvice в версии 3.2, где в нашем приложении можно создать класс Controller Advice, который будет способен обрабатывать все глобальные сценарии исключений.
Класс, аннотированный @ControllerAdvice, будет зарегистрирован как глобальный обработчик исключений .
Давайте создадим один для нашего приложения.
ExceptionControllerAdvice.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
package com.jcombat.controller; 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 com.jcombat.bean.ErrorResponse; @ControllerAdvicepublic class ExceptionControllerAdvice { @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex) { ErrorResponse error = new ErrorResponse(); error.setErrorCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); error.setMessage("Please contact your administrator"); return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK); }} |
Это означает, что если мы когда-либо получим непредвиденное исключение в нашем приложении, отличное от пользовательского исключения, будет подготовлен общий объект ошибки с общим кодом ошибки и сообщением об ошибке, которое будет возвращено как ответ JSON об ошибке.
В версии Spring более ранней, чем 3.2, лучшей альтернативой было бы создание с одним базовым контроллером с расширением всех отдельных контроллеров вместо @ControllerAdvice .
Здесь есть что отметить. Ошибка ответа JSON не может быть возвращена в Spring 3.0.x с ResponseEntity из-за отсутствия поддержки, предоставляемой Spring 3.0.x. Альтернативой для этого было бы использование BeanNameViewResolver с ModelAndView в качестве возвращаемого типа. Вскоре мы придумаем пример приложения по этому вопросу.
4. Запуск приложения
Время запустить приложение, которое мы создали.
Убедитесь, что мы опубликовали приложение на сервере и запустили его.
Теперь нажмите на указанный ниже URI в браузере — http: // localhost: 8080 / RESTWithSpringMVCException / Ramesh? EmpId = 1234
Давайте посмотрим, как выглядит ответ об ошибке. Обратите внимание, что мы добавили блок IF в EmployeeController , который проверяет, является ли переменная пути для имени сотрудника числовой. Если он числовой, наше приложение выдает исключение EmployeeException . Давайте ударим ниже URI —
Если нам нужно добавить электронную почту в качестве одной из переменных пути, лучше пройти через приложение-службу RESTful, которое мы создали в предыдущем уроке .
5. Загрузите исходный код
| Ссылка: | Обработка исключений весной RESTful Web Service от нашего партнера JCG Абхиманью Прасада в блоге jCombat . |

