Spring Boot Validation
SpringBoot 参数校验
校验框架
Bean 校验框架的事实标准:Hibernate Validator
依赖
从 SpringBoot 2.3 开始,需要显式地引入
spring-boot-starter-validation
依赖
领域模型
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users") // user 是数据库的保留字
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotBlank(message = "Name is mandatory")
private String name;
@NotBlank(message = "Email is mandatory")
private String email;
}
使用 @NotBlank
注解表示 name 和 email 的值不能为空。
数据访问层:
public interface UserRepository extends JpaRepository<User, Long> {}
控制器层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public User addUser(@Valid @RequestBody User user) {
return userService.addUser(user);
}
}
当 SpringBoot 发现一个带有 @Valid
注解的参数,它会自动装配 Hibernate Validation 并校验该参数。
在校验失败时,抛出 MethodArgumentNotValidException
异常。
全局异常处理
/**
* 全局校验异常处理
* */
@ControllerAdvice
public class ValidExceptionHandler {
/**
* 以 JSON 格式返回校验异常信息
* */
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public Map<String, String> handleValidationException(MethodArgumentNotValidException e) {
Map<String, String> errors = new HashMap<>();
e.getBindingResult()
.getAllErrors()
.forEach(error -> {
String fieldName = ((FieldError)error).getField(); // 字段名
String errorMessage = error.getDefaultMessage(); // 校验失败信息
errors.put(fieldName, errorMessage);
});
return errors;
}
}
测试
@WebMvcTest
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
private ObjectMapper objectMapper = new ObjectMapper();
/**
* 参数校验成功测试
* */
@Test
public void whenPostRequestToUserAndValidUser_thenCorrectResponse() throws Exception {
User user = User.builder()
.name("Alex")
.email("alex@gamil.com")
.build();
String json = objectMapper.writeValueAsString(user);
mockMvc.perform(post("/user")
.content(json)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
/**
* 参数校验失败测试
* */
@Test
public void whenPostRequestToUserAndInvalidUser_thenCorrectResponse() throws Exception {
User userWithoutName = User.builder()
.email("alex@gmail.com")
.build();
String json = objectMapper.writeValueAsString(userWithoutName);
mockMvc.perform(post("/user")
.content(json)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.name", Is.is("Name is mandatory")));
}
}
校验注解
注解 | 适用类型 | 说明 |
---|---|---|
@Null | 任何类型 | |
@NotNull | 任何类型 | |
@NotEmpty | 字符串、集合、Map | 元素不能为 null 且不能为空 |
@NotBlank | 字符串 | 字符串不能为 null 并且不能只包含空白字符 |
@Size(min=, max=) | 字符串、集合、Map | 容器大小,包含边界 |
@Max(value=) | 数值 | 最大值 |
@Min(value=) | 数值 | 最小值 |
@Digits(integer=, fraction=) | 数值或字符串 | 数位 |
@Negative | 数值 | 负数,不能是零 |
@NegativeOrZero | 数值 | 负数或零 |
@Positive | 数值 | 正数,不能是零 |
@PositiveOrZero | 数值 | 正数或零 |
@Pattern(regex=, flags=) | 字符串 | 字符串符合正则表达式 |
字符串 | ||
@DecimalMax(value=, inclusive=) | 数值或字符串 | 最大值 |
@DecimalMin(value=, inclusive=) | 数值或字符串 | 最小值 |
@Future | 日期类型 | 将来日期 |
@FutureOrPresent | 日期类型 | 现在或将来日期 |
@Past | 日期类型 | 过去日期 |
@PastOrPresent | 日期类型 | 过去或现在日期 |
@AssertFalse | 布尔值 | |
@AssertTrue | 布尔值 |
数值包括:BigDecimal, BigInteger, byte, short, int, long