tonglin0325的个人主页

SpringBoot学习笔记——校验

JSR-303提供了一些注解,将其放到属性上,可以限制这些属性的值。

参考:Spring MVC学习笔记——JSR303介绍及最佳实践

校验放在DTO层上,不要和数据库交互的model层混用

关于model,VO等的区别,参考:Spring MVC学习笔记——POJO和DispatcherServlet

如何赋值,参考:优雅的使用BeanUtils对List集合的操作

DTO和DO的转换,可以使用BeanUtils,参考:设计之道-controller层的设计

也可以使用ModelMapper,参考:Spring Boot DTO示例:实体到DTO的转换

如果使用的springboot版本大于2.3.x,需要额外引用依赖

1
2
3
4
5
6
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.1.Final</version>
</dependency>

参考:使用SpringBoot进行优雅的数据验证

定义dto层或者vo层,添加 @NotEmpty注解 和 @Size注解,并设置分组校验,即在Post请求或者Put请求的时候进行校验

1
2
3
4
5
6
7
8
9
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
import com.example.demo.core.valid.Post;
import com.example.demo.core.valid.Put;
import com.example.demo.model.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {

@NotEmpty(groups = Post.class, message = "注册时username字段不能为空")
@Size(groups = {Post.class, Put.class}, min = 3, max = 120)
private String username;

private String password;

public static User convert(UserDTO dto) {
User user = new User();
BeanUtils.copyProperties(dto, user);
return user;
}

public static UserDTO convertDTO(User user) {
UserDTO dto = new UserDTO();
BeanUtils.copyProperties(user, dto);
return dto;
}

}

定义Post分组接口

1
2
3
4
5
package com.example.demo.core.valid;

public interface Post {
}

定义Put分组接口

1
2
3
4
5
package com.example.demo.core.valid;

public interface Put {
}

设置全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ControllerResponseT methodArgumentNotValidException(MethodArgumentNotValidException e) {
String message = ResultCode.METHOD_ARGUMENT_NOT_VALID.getMessage();
log.error("参数验证失败 => {}", e.getMessage());
BindingResult bindingResult = e.getBindingResult();
List<ObjectError> allErrors = bindingResult.getAllErrors();
return new ControllerResponseT<>(ResultCode.METHOD_ARGUMENT_NOT_VALID.getCode(), message, allErrors);
}

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler({SQLException.class, DataAccessException.class})
public ControllerResponseT databaseException(final Throwable e) {
String message = ResultCode.DATABASE_ERROR.getMessage();
log.error("数据库错误 => {}", e.getMessage());
return ControllerResponseT.ofFail(ResultCode.DATABASE_ERROR.getCode(), message, e.getMessage());
}

controller层

1
2
3
4
5
6
7
8
@ApiImplicitParams({
@ApiImplicitParam(paramType = "body", dataType = "UserDTO", name = "userDTO", value = "用户", required = true)
})
@RequestMapping(path = "/user", method = RequestMethod.POST)
public ControllerResponseT create(@Validated({Post.class}) @RequestBody UserDTO userDTO) {
int result = userService.save(UserDTO.convert(userDTO));
return ControllerResponseT.ofSuccess("success");
}

如果参数验证错误,则接口返回结果如下