接口方法往往需要对入参做一些校验,从而判断入参是否合格,而javax.validation包为我们提供了一些常用的参数校验注解,使用起来很方便。
下面这个示例是检验入参对象中的password是否为空
1. 创建一个User.java
import javax.validation.constraints.NotBlank; public class User { private String username; @NotBlank private String password; private Integer age; }
2. 接口方法
@PostMapping("/user") public User createUser(@Valid @RequestBody User user) { System.out.println(user); user.setAge(100); return user; }
3. 测试代码
@Test public void testCreateUser() throws Exception { String jsonUser = "{\"username\":\"admin\",\"password\":null}"; mockMvc.perform(MockMvcRequestBuilders.post("/user") .contentType(MediaType.APPLICATION_JSON) .content(jsonUser)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.age").value("100")); }
4. 测试结果
在测试代码中, 我们的password=null, 所以检验不通过,报400。而password不为null ,测试通过,如下
5. BindingResult
在以上的参数校验中,如果参数校验不通过,都不通进入接口方法,我们也不好收集错误信息.。 不过,spring为我们提供了一个java类BindingResult.java, 借助它即可!
注意: @Valid与BindingResult要配套使用
5.1 接口代码
@PostMapping("/user") public User createUser(@Valid @RequestBody User user, BindingResult errors) { if (errors.hasErrors()) { errors.getAllErrors().stream().forEach(x-> System.out.println(x.getDefaultMessage())); } System.out.println(user); user.setAge(100); return user; }
5.2 测试代码及打印结果
5.3 自定义错误信息
public class User { private String username; @NotBlank(message = "密码不能为空") private String password; private Integer age; }
6. 自定义注解
javax.validation默认提供了不少的注解,但是有时候,我们需要按照自己的逻辑去自定义注解。
下面举个例子,在创建User对象时,先校验一下数据库是否存在相同的用户名username, 代码如下:
6.1 查询逻辑
@Service public class UserService { /** * 模拟查询,检验admin用户名是否存在 * @param username * @return */ public boolean findUserByUsername(String username) { if (StringUtils.equals(username, "admin")) { return false; } return true; } }
6.2 自定义注解及实现
import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) // 该注解的具体实例逻辑类 @Constraint(validatedBy = MyValidatorImpl.class) public @interface MyValidator { String message() default "admin用户已存在"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
import org.springframework.beans.factory.annotation.Autowired; import qinfeng.zheng.mockmvcdemo.service.UserService; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * 实现 ConstraintValidator接口的类,spring会将其弄成一个bean */ public class MyValidatorImpl implements ConstraintValidator<MyValidator, String> { @Autowired private UserService userService; @Override public void initialize(MyValidator anno) { System.out.println("初始化。。。。"); } /** * @param value 需要校验的参数值 * @param constraintValidatorContext * @return false : 校验不通过 * true : 校验通过 */ @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { return userService.findUserByUsername(value); } }
6.3 实体类上注解的使用
public class User { @MyValidator private String username; @NotBlank(message = "密码不能为空") private String password; private Integer age; }
6.4 校验接口
@PostMapping("/user") public User createUser(@Valid @RequestBody User user, BindingResult errors) { if (errors.hasErrors()) { errors.getAllErrors().stream().forEach(x-> System.out.println(x.getDefaultMessage())); } System.out.println(user); user.setAge(100); return user; }
6.5 测试代码
日拱一卒无有尽,功不唐捐终入海