默认情况下,如果发生Exception并且代码中未做处理,Spring Boot将进行Exception捕获,并将返回格式正确的JSON或XML消息返回给调用客户端的应用程序。 例如下面的一个:
{
"timestamp": "2020-011-04T23:38:30.376+0000",
"status": 500,
"error": "Internal Server Error",
"message": "could not execute statement; SQL [n/a]; constraint [UK_6dotkott2kjsp8vw4d0m25fb7]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
"path": "/users"
}
这样的问题是返回的错误可能暴露了我们后端的一部分逻辑并且对最终使用者来说没有什么意义。
如果抛出自定义异常怎么办
如果要抛出自自定义Exception,则调用客户端的应用程序将获得完全相同的JSON或XML结构。 错误消息和时间戳值当然会有所不同,但返回的文档的结构将相同。 因此,无需创建它并以编程方式设置其值。
自定义Runtime Exception
package com.xarhsoft.app.ws.exceptions;
public class UserServiceException extends RuntimeException{
private static final long serialVersionUID = 5776681206288518465L;
public UserServiceException(String message)
{
super(message);
}
}
使用自定义Exception
@Override
public UserDetails loadUserByUsername(String username) throws UserServiceException {
UserEntity userEntity = userRepository.findUserByEmail(username);
if (userEntity == null) {
throw new UserServiceException("User " + username + " not found");
}
return new User(userEntity.getEmail(), userEntity.getEncryptedPassword(), new ArrayList<>());
}
返回结果如下:
{
"timestamp": "2018-04-18T23:51:54.405+0000",
"status": 500,
"error": "Internal Server Error",
"message": "User sergey not found",
"path": "/users/sergey"
}
捕获任意异常
可以使用Spring Boot捕获任何地方发生的任何异常。具体方法如下:
定义全局异常类
- 创建用
@ControllerAdvice
注解的类- 添加一个用
@ExceptionHandler
注解的方法,指定此方法需要处理的Exception类。
package com.xarhsoft.app.ws.exceptions;
import org.springframework.http.HttpHeaders;
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 org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice
public class EntityExceptionHandler {
@ExceptionHandler(value = { Exception.class })
public ResponseEntity<Object> handleAnyException(Exception ex, WebRequest request) {
return new ResponseEntity<>(
ex.getMessage(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
返回自定义错误消息体
要使返回的错误消息具有特定的JSON或XML结构,可以创建一个Java bean类并将其用作响应实体。
import java.util.Date;
public class ErrorMessage {
private Date timestamp;
private String message;
private String details;
public ErrorMessage(){}
public ErrorMessage(Date timestamp, String message, String details) {
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
现在,使用此自定义ErrorMessage类作为Exception Handler类中的响应实体:
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 org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.Date;
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorMessage> handleAllExceptions(Exception ex, WebRequest request) {
ErrorMessage errorObj = new ErrorMessage(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorObj, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
捕获运行时异常
如果抛出此异常,可以使用下面的异常处理程序类来处理它。 为了使其能够处理我们自己的特定自定义异常,我们只需在@ExceptionHandler
批注中指定异常的名称即可。
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 org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.Date;
@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(UserServiceException.class)
public final ResponseEntity<ErrorMessage> handleSpecificExceptions(Exception ex, WebRequest request) {
ErrorMessage errorMessage = new ErrorMessage(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorMessage, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
抛出多个异常
如果需要一种方法来处理多个异常,则可以指定这些异常,并在@ExceptionHandler
注解内用逗号将其分隔。 例如:
@ExceptionHandler(UserNotFoundException.class, RecordExistsException.class)
完整类实现:
@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(UserNotFoundException.class, RecordExistsException.class)
public final ResponseEntity<ErrorMessage> handleUserExceptions(Exception ex, WebRequest request) {
ErrorMessage errorMessage = new ErrorMessage(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorMessage, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
在@RestController
内实现异常捕获
可以在引发该异常的同一个类中处理异常。 例如,可以让@RestController
类中的方法之一处理异常消息。
这是一个非常简单的带有@RestController
注释的Root Resource
类的示例,该类立即抛出异常,以演示如何在同一类中对其进行处理。
@RestController
@RequestMapping("users")
public class UserController {
@Autowired
UserService userService;
@PostMapping
public UserRest createUser(@RequestBody UserDetailsRequestModel requestUserDetails) {
UserRest returnValue = new UserRest();
/// 现在仅出于演示目的就抛出异常
if(true) throw new MissingRequiredFieldException("One of the required fields is missing");
/
UserDto userDto = new UserDto();
BeanUtils.copyProperties(requestUserDetails, userDto);
UserDto createdUser = userService.createUser(userDto);
BeanUtils.copyProperties(createdUser, returnValue);
returnValue.setHref("/users/" + createdUser.getUserId());
return returnValue;
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingRequiredFieldException.class)
public ErrorMessage handleBadRequest(HttpServletRequest req, Exception ex) {
return new ErrorMessage(new Date(), ex.getMessage(), req.getRequestURI());
}
}