接口参数校验之@Valid与BindingResult

接口方法往往需要对入参做一些校验,从而判断入参是否合格,而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 测试代码

原文地址:https://www.cnblogs.com/z-qinfeng/p/11746260.html