目录
问题
使用 spring boot 开发 web 项目时,基本上每一个接口都要做异常捕捉处理和入参校验,如果每个都硬编码在每个接口中,会导致程序中存在大量重复的代码。
冗余代码写法
@RequestMapping("/valid")
public ResponseEntity<UserDTO> valid(@RequestBody UserDTO dto){
try {
//校验入参
Assert.notNull(dto.getName(), "name 不能为空");
return ResponseEntity.ok(dto);
} catch (Exception e) {
//记录日志
throw new RuntimeException(e);
}
}
如何解决
配置 spring 的全局异常处理(@RestControllerAdvice)
使用 validation 校验入参(@Validated 和 @Valid),@Validated 没法作用在类成员对象上,而 @Valid 可以,在实际业务中经常会遇到类成员变量为其他业务对象的情况,所以使用 @Valid 替代 @Validation
示例代码
pom
<properties>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
公共结果类
@Data
public class ResultVO<T> {
private static String SUCCESS_CODE = "200000";
private static String SUCCESS_MSG = "请求成功";
private static String ERROR_CODE = "000000";
private static String ERROR_MSG = "请求错误";
private String code;
private String msg;
private T data;
public ResultVO(String code, String message) {
this.code = code;
this.msg = message;
}
public ResultVO(String code, String message, T data) {
this.code = code;
this.msg = message;
this.data = data;
}
public static <T> ResultVO<T> success() {
return new ResultVO<>(SUCCESS_CODE, SUCCESS_MSG);
}
public static <T> ResultVO<T> success(String msg) {
return new ResultVO<>(SUCCESS_CODE, msg);
}
public static <T> ResultVO<T> success(T data) {
return new ResultVO<>(SUCCESS_CODE, SUCCESS_MSG, data);
}
public static <T> ResultVO<T> success(String code, String msg) {
return new ResultVO<>(code, msg);
}
public static <T> ResultVO<T> success(String code, String msg, T data) {
return new ResultVO<>(code, msg, data);
}
public static <T> ResultVO<T> error() {
return new ResultVO<>(ERROR_CODE, ERROR_MSG);
}
public static <T> ResultVO<T> error(String msg) {
return new ResultVO<>(ERROR_CODE, msg);
}
public static <T> ResultVO<T> error(T data) {
return new ResultVO<>(ERROR_CODE, ERROR_MSG, data);
}
public static <T> ResultVO<T> error(String code, String msg) {
return new ResultVO<>(code, msg);
}
public static <T> ResultVO<T> error(String code, String msg, T data) {
return new ResultVO<>(code, msg, data);
}
}
全局异常配置类
@Slf4j
@RestControllerAdvice
public class ExceptionConfiguration {
/**
* body json 校验异常捕获
*
* @param e 错误信息集合
* @return ResultVO
*/
@ExceptionHandler (MethodArgumentNotValidException.class)
public ResultVO<?> validationBodyException(MethodArgumentNotValidException e) {
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(joining(";"));
log.error("捕获异常: {}", e);
return ResultVO.error(message);
}
/**
* form 校验异常捕获
*
* @param e 错误信息集合
* @return ResultVO
*/
@ExceptionHandler(value = BindException.class)
public ResultVO<?> bindExceptionHandler(BindException e) {
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(joining(";"));
log.error("捕获异常: {}", e);
return ResultVO.error(message);
}
/**
* query param 校验异常捕获
*
* @param e 错误信息集合
* @return ResultVO
*/
@ExceptionHandler(value = ConstraintViolationException.class)
public ResultVO<?> constraintViolationExceptionHandler(ConstraintViolationException e) {
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
String message = constraintViolations.stream().map(ConstraintViolation::getMessage)
.collect(joining(","));
log.error("捕获异常: {}", e);
return ResultVO.error(message);
}
/**
* 兜底异常处理
*
* @param e 兜底异常
* @return ResultVO
*/
@ExceptionHandler (Exception.class)
public ResultVO<?> runtimeException(Exception e) {
log.error("捕获异常", e);
return ResultVO.error(e.getMessage());
}
}
Controller
@Slf4j
@RestController
@RequestMapping ("test")
public class TestController {
@RequestMapping("/valid")
public ResponseEntity<UserDTO> valid(@RequestBody @Valid UserDTO dto){
return ResponseEntity.ok(dto);
}
}
DTO
public class UserDTO {
@NotNull(message = "用户id不能为空")
private Integer id;
@NotEmpty(message = "用户名不能为空")
@Size(min = 4, max = 30, message = "用户名只能在4~30位之间")
private String name;
@NotEmpty(message = "密码不能为空")
private String password;
@Min(message = "年龄最小为18岁", value = 18)
@Max(message = "年龄最大为80岁", value = 80)
@NotNull(message = "年龄不能为空")
private int age;
@Pattern(regexp = "^1[35678]\\d{9}$", message = "手机号格式不正确")
@NotBlank(message = "手机号不能为空")
private String phone;
@NotBlank(message = "邮箱不能为空")
@Email(message = "请输入正确的邮箱")
private String email;
@Valid
private InnerClass innerClass;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public InnerClass getInnerClass() {
return innerClass;
}
public void setInnerClass(InnerClass innerClass) {
this.innerClass = innerClass;
}
public static class InnerClass {
@NotBlank(message = "innerField不能为空")
private String innerField;
public String getInnerField() {
return innerField;
}
public void setInnerField(String innerField) {
this.innerField = innerField;
}
}
}