предисловие
в повседневной жизниweb
Когда во время разработки возникает исключение, часто необходимо передать унифицированныйОбработка исключений, чтобы клиент мог получать дружественные подсказки. Эта статья познакомитSpring Boot
серединаГлобальная унифицированная обработка исключений.
Статьи из этой серии
- Actual Spring Boot 2.0 Series (1) — Создание образов Docker с помощью Gradle
- Actual Spring Boot 2.0 Series (2) — глобальная обработка исключений и тестирование
- Actual Spring Boot 2.0 Series (3) — Подробное объяснение асинхронных вызовов с использованием @Async
- Actual Spring Boot 2.0 Series (4) — Использование WebAsyncTask для обработки асинхронных задач
- Actual Spring Boot 2.0 Series (5) — прослушиватель, сервлет, фильтр и перехватчик
- Actual Spring Boot 2.0 Series (6) — несколько реализаций одномашинных задач синхронизации
текст
1. Создайте проект
использоватьSpring Initializer
Создаватьgradle
проектspring-boot-global-exception-handle
, добавляйте связанные зависимости при создании. получить начальныйbuild.gradle
следующее:
buildscript {
ext {
springBootVersion = '2.0.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'io.ostenant.springboot.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.projectlombok:lombok')
compile('org.apache.commons:commons-lang3:3.1')
compile('com.google.guava:guava:19.0')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
2. Настройте класс входа
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. Настройте класс сущности
Установить первымIntellij Idea
изlombok
Плагины, которые подробно здесь не описаны. Помните, вам нужно установитьEnable annotation processing
Проверьте это, иначетестовый кодсуществуетвремя компиляциине быть способным сделатьlombok
Плагин настроенаннотациядля обработки.
использоватьlombok
предоставляется инструментоманнотацияНастройка класса сущностей
import lombok.Data;
@Data
public class User implements Serializable {
private Long id;
private String username;
private String accountName;
}
4. Настройте объект ответа на исключение
ErrorMessage
Сущности используются для записи конкретныхинформация об исключении, и ответьтеклиент.
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@Setter
@Getter
@ToString
public class ErrorMessage<T> {
public static final Integer OK = 0;
public static final Integer ERROR = 100;
private Integer code;
private String message;
private String url;
private T data;
}
5. Настройте связанные классы исключений
SessionNotFoundException.java
public class SessionNotFoundException extends Exception {
@Getter
@Setter
protected String message;
public SessionNotFoundException() {
setMessage("Session is not found!");
}
public SessionNotFoundException(String message) {
this.message = message;
}
}
NullOrEmptyException.java
public class NullOrEmptyException extends Exception {
@Getter
@Setter
protected String message;
public NullOrEmptyException() {
setMessage("Parameter is null or empty!");
}
public NullOrEmptyException(String message) {
this.message = message;
}
}
IllegalPropertiesException.java
public class IllegalPropertiesException extends Exception {
@Getter
@Setter
protected String message;
public IllegalPropertiesException() {
setMessage("Prop is illegal!");
}
public IllegalPropertiesException(String message) {
this.message = message;
setMessage(String.format("Prop: %s is illegal!", message));
}
}
6. Настройте глобальное уведомление об исключении
отspring 3.2
начало, добавлено@ControllerAdvice
Аннотация, которую можно использовать для определения@ExceptionHandler
, и применяется к конфигу@RequestMapping
в контроллере.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(SessionNotFoundException.class)
@ResponseBody
public ErrorMessage<String> sessionNotFoundExceptionHandler(HttpServletRequest request, SessionNotFoundException exception) throws Exception {
return handleErrorInfo(request, exception.getMessage(), exception);
}
@ExceptionHandler(NullOrEmptyException.class)
@ResponseBody
public ErrorMessage<String> nullOrEmptyExceptionHandler(HttpServletRequest request, NullOrEmptyException exception) throws Exception {
return handleErrorInfo(request, exception.getMessage(), exception);
}
@ExceptionHandler(IllegalPropertiesException.class)
@ResponseBody
public ErrorMessage<String> illegalPropExceptionHandler(HttpServletRequest request, IllegalPropertiesException exception) throws Exception {
return handleErrorInfo(request, exception.getMessage(), exception);
}
@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorMessage<String> exceptionHandler(HttpServletRequest request, Exception exception) throws Exception {
return handleErrorInfo(request, exception.getMessage(), exception);
}
private ErrorMessage<String> handleErrorInfo(HttpServletRequest request, String message, Exception exception) {
ErrorMessage<String> errorMessage = new ErrorMessage<>();
errorMessage.setMessage(message);
errorMessage.setCode(ErrorMessage.ERROR);
errorMessage.setData(message);
errorMessage.setUrl(request.getRequestURL().toString());
return errorMessage;
}
}
Приведенный выше код указывает3
Кусокконкретныйобработчик исключений и1
Кусокдефолтобработчик исключений. Когда при обработке запроса возникает исключение,обработчик исключенийизПорядок конфигурациипопробуй по порядкуненормальное совпадениеииметь дело с.
Если исключение не находится в SessionNotFoundException, NullOrEmptyException, IllegalPropertiesException,Spring
доверитдефолтизexceptionHandler
для обработки.
7. Настройте контроллер
В зависимости от разницы запрошенных данных, контроллер может покрыть вышеперечисленное3
Путь обработки исключений.
@RestController
public class UserController {
@PostMapping("user")
public ResponseEntity<?> save(HttpServletRequest request, HttpSession session) throws Exception {
String sessionId = (String) session.getAttribute("sessionId");
if (StringUtils.isBlank(sessionId)) {
throw new SessionNotFoundException();
}
String userPlainText = request.getParameter("user");
if (StringUtils.isBlank(userPlainText) || StringUtils.equalsIgnoreCase("{}", userPlainText)) {
throw new NullOrEmptyException();
}
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(userPlainText, User.class);
if (StringUtils.isBlank(user.getUsername())) {
throw new IllegalPropertiesException("username");
}
if (StringUtils.isBlank(user.getAccountName())) {
throw new IllegalPropertiesException("accountName");
}
return ResponseEntity.ok("Successful");
}
}
8. Настройте класс фиктивного теста
Spring Mock
Соответствующая конфигурация здесь подробно не описывается, следующие тестовые классы охватываютUserController
всех путей выполнения.
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootApplication
@WebAppConfiguration
@Slf4j(topic = "UserControllerTester")
public class ApplicationTests {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
private MockHttpSession session;
@Autowired
private UserController userController;
private ImmutableMap<Long, Pair<String, String>> map = new ImmutableMap.Builder<Long, Pair<String, String>>()
.put(0x00001L, Pair.of("user", ""))
.put(0x00002L, Pair.of("user", "{}"))
.put(0x00003L, Pair.of("user", "{\"username\": \"\", \"accountName\": \"\"}"))
.put(0x00004L, Pair.of("user", "{\"username\": \"Harrison\", \"accountName\": \"\"}"))
.put(0x00005L, Pair.of("user", "{\"username\": \"Harrison\", \"accountName\": \"ostenant\"}"))
.build();
@Before
public void setUp() throws Exception {
boolean singleRunner = false;
if (singleRunner) {
this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
} else {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
session = new MockHttpSession();
session.setAttribute("sessionId", StringUtils.replace(UUID.randomUUID().toString(), "-", ""));
log.debug("sessionId: {}", session.getAttribute("sessionId"));
}
/**
* 测试SessionNotFoundException
* @throws Exception
*/
@Test
public void testSessionNotFoundException() throws Exception {
session.clearAttributes();
// 模拟发送请求
mockMvc.perform(
MockMvcRequestBuilders.post("/user")
.param(map.get(0x00005L).getKey(), map.get(0x00005L).getValue())
.session(session))
.andExpect(MockMvcResultMatchers.handler().handlerType(UserController.class))
.andExpect(MockMvcResultMatchers.handler().methodName(("save")))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
/**
* 测试NullOrEmptyException
* @throws Exception
*/
@Test
public void testNullOrEmptyException() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.post("/user")
.param(map.get(0x00001L).getKey(), map.get(0x00001L).getValue())
.session(session))
.andExpect(MockMvcResultMatchers.handler().handlerType(UserController.class))
.andExpect(MockMvcResultMatchers.handler().methodName(("save")))
.andDo(MockMvcResultHandlers.print())
.andReturn();
mockMvc.perform(
MockMvcRequestBuilders.post("/user")
.param(map.get(0x00002L).getKey(), map.get(0x00002L).getValue())
.session(session))
.andExpect(MockMvcResultMatchers.handler().handlerType(UserController.class))
.andExpect(MockMvcResultMatchers.handler().methodName(("save")))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
/**
* 测试IllegalPropException
* @throws Exception
*/
@Test
public void testIllegalPropException() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.post("/user")
.param(map.get(0x00003L).getKey(), map.get(0x00003L).getValue())
.session(session))
.andExpect(MockMvcResultMatchers.handler().handlerType(UserController.class))
.andExpect(MockMvcResultMatchers.handler().methodName(("save")))
.andDo(MockMvcResultHandlers.print())
.andReturn();
mockMvc.perform(
MockMvcRequestBuilders.post("/user")
.param(map.get(0x00004L).getKey(), map.get(0x00004L).getValue())
.session(session))
.andExpect(MockMvcResultMatchers.handler().handlerType(UserController.class))
.andExpect(MockMvcResultMatchers.handler().methodName(("save")))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
/**
* 测试正常运行的情况
* @throws Exception
*/
@Test
public void testNormal() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.post("/user")
.param(map.get(0x00005L).getKey(), map.get(0x00005L).getValue())
.session(session))
.andExpect(MockMvcResultMatchers.handler().handlerType(UserController.class))
.andExpect(MockMvcResultMatchers.handler().methodName(("save")))
.andDo(MockMvcResultHandlers.print())
.andReturn();
}
}
9. Результаты испытаний
Запускайте тесты партиями, результаты тестов будут следующими, и все тестовые примеры пройдены.
резюме
использовать@ControllerAdvice
Обработка исключений также имеет определенныеограничение. только введитеController
ошибки слоя будут вызваны@ControllerAdvice
иметь дело с.перехватчиквыдана ошибка, иДоступ к неправильному адресуСлучай@ControllerAdvice
не может быть обработанSpring Boot
дефолтмеханизм обработки исключенийиметь дело с.
Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack
Эта учетная запись будет продолжать делиться сухими товарами серверных технологий, включая основы виртуальных машин, многопоточное программирование, высокопроизводительные фреймворки, асинхронное ПО, промежуточное ПО для кэширования и обмена сообщениями, распределенные и микросервисы, материалы для обучения архитектуре и расширенные учебные материалы и статьи.