必须要知道

  • 简述 JSR303/JSR-349,hibernate validation,spring validation 之间的关系
    JSR303 是一项标准,JSR-349 是其的升级版本,添加了一些新特性,他们规定一些校验规范即校验注解,如 @Null,@NotNull,@Pattern,他们位于 javax.validation.constraints 包下,只提供规范不提供实现;
    hibernate validation 是对这个规范的实践,他提供了相应的实现,并增加了一些其他校验注解,如 @Email,@Length,@Range 等等,他们位于 org.hibernate.validator.constraints 包下;
    spring validation,是对 hibernate validation 进行了二次封装,在 springmvc 模块中添加了自动校验,并将校验信息封装进了特定的类中;

spring mvc对spring validation的应用

get请求 参数验证

@RestController
@RequestMapping("/beavalidate")
public class BeanValidateController {

    @GetMapping("/testget")
    public ResultBody testGet(@Valid @NotBlank @RequestParam("name") String name){
        return ResultBody.successBody("get方式验证成功!");
    }
}

post请求 参数验证

@RestController
@RequestMapping("/beavalidate")
public class BeanValidateController {

    @PostMapping("/testpost")
    public ResultBody testPost(@Valid  @RequestBody TagAddForm tagAddForm){
        return ResultBody.successBody("验证成功!");
    }
}

参数验证异常统一处理

如果我们没有对验证错误进行处理,调用接口的客户端无法知道到底是什么参数发生了错误,
还有就是如果前后端分离我们一般都是规定json格式传输数据,所以我们最好针对这个错误进行处理并返回格式化的内容。

定义错误相关的枚举

public interface IResult extends Serializable {
    
    Integer getCode();

    String getMessage();
}

public enum ResultEnum implements IResult {
    /**
     * 成功
     */
    SUCCESS(10000, "成功"),
    /**
     * 请求参数异常
     */
    ARGUMENT_EXCEPTION(10001, "请求信息异常");
	
    private Integer code;
    private String message;

    ResultEnum(Integer code, String msg) {
        this.code = code;
        this.message = msg;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

定义统一的json对象

@Data
public class ResultBody<T> {
    /**
     * 错误码
     */
    private Integer code;

    /**
     * 提示信息
     */
    private String message;

    /**
     * 具体数据
     */
    private T body;

    private ResultBody() {

    }

    /**
     * 构造成功包体
     *
     * @return 成功的包体
     */
    public static ResultBody successBody() {
        return dowith(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), new HashMap<>(1));
    }

    /**
     * 构造成功的包体
     *
     * @param body 包体中body的内容
     * @return 成功的包体
     */
    public static ResultBody successBody(Object body) {
        return dowith(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), body);
    }

    /**
     * 构造调用失败的包体
     *
     * @param result 错误码和提示信息内容
     * @return 失败的包体
     */
    public static ResultBody errorBody(IResult result) {
        return dowith(result.getCode(), result.getMessage(), new HashMap<>(1));
    }

    /**
     * 构造调用失败的包体
     *
     * @param result 错误码和提示信息内容
     * @param body   包体内容
     * @return 失败的包体
     */
    public static ResultBody errorBody(IResult result, Object body) {
        return dowith(result.getCode(), result.getMessage(), body);
    }

    /**
     * 构造调用失败的包体
     *
     * @param result 错误码和提示信息内容
     * @return 失败的包体
     */
    public static ResultBody errorBody(IResult result, String message) {
        return dowith(result.getCode(), message, null);
    }

    /**
     * 构造调用失败的包体
     *
     * @param result 错误码和提示信息内容
     * @param body   包体内容
     * @return 失败的包体
     */
    public static ResultBody errorBody(IResult result, Object body, String message) {
        return dowith(result.getCode(), message, body);
    }

}

定义异常处理器

@ControllerAdvice
public class ValidationExceptionHandler {
    
	/**
	 * 处理post方式参数验证异常
     * @param e 
     * @return
     */
	@ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseBody
    public ResultBody notValidException(MethodArgumentNotValidException e) {
        log.error("API参数校验异常:{}", e);
        return this.wrapperBindingResult(e.getBindingResult());
    }
	
	/**
	 * 处理get方式参数验证异常
     * @param e 
     * @return
     */
	@ResponseBody
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public ResultBody requestMissingParamsErrorHandler(MissingServletRequestParameterException e) {
        log.error("MissingServletRequestParameterException:{}", e);
        String errorMessage = e.getMessage();
        ResultEnum resultEnum = ResultEnum.ARGUMENT_EXCEPTION;

        return ResultBody.errorBody(resultEnum);
    }
	
	private ResultBody wrapperBindingResult(BindingResult bindingResult) {
        ResultEnum resultEnum = ResultEnum.ARGUMENT_EXCEPTION;
        StringBuilder errorMessage = new StringBuilder();
        if ("prod".equals(this.profile)) {
            errorMessage.append("请求信息异常");
        } else {
            Iterator var4 = bindingResult.getFieldErrors().iterator();

            while(var4.hasNext()) {
                FieldError fieldError = (FieldError)var4.next();
                errorMessage.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(";");
            }
        }

        resultEnum.setMessage(errorMessage.toString());
        return ResultBody.errorBody(resultEnum);
    }
}

效果

  • get方式参数验证
  • spring boot 请求信息记录 spring boot 请求参数_提示信息

  • post方式参数验证:
  • spring boot 请求信息记录 spring boot 请求参数_spring_02

非mvc环境下使用验证

在一些情况下我们并不是只是验证http请求参数的绑定,我们还需要java方法之间调用的参数验证。
不适用验证框架我们一般都是手动验证,但是手动验证是很费力的,下面就介绍一下在非mvc的情况下使用spring validation框架进行验证。

手动验证

定义bean添加验证注解

@Data
public class TagAddForm {

    @NotEmpty(message = "标签名称不能为空")
    private String name;

    @Min(value = 1,message = "标签类型不能为空")
    private int type;

}

编写手动验证逻辑

@Component
public class TagValidate {


    /**
     * 验证bean的参数
     * <p>
     *     手动验证
     * </p>
     */
    public void validateBean(){
        TagAddForm tagAddForm=new TagAddForm();
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<TagAddForm>> violations = validator.validate(tagAddForm);
        //为空代表验证通过
        if (violations.size()==0){
            return;
        }
        for(ConstraintViolation<TagAddForm> violation: violations) {
            System.out.println((violation.getMessage()));
        }
    }


}

单元测试

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestValidate {
    @Autowired
    private TagValidate tagValidate;
    @Autowired
    private UserValidate userValidate;

    @Test
    public void test(){
        tagValidate.validateBean();
    }
}

结果:

spring boot 请求信息记录 spring boot 请求参数_提示信息_03

自动验证

业务类编写

在类上添加注解:@Validated 在方法上添加:@Valid注解

import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;

@Validated
@Component
public class UserValidate {

    public void add(@Valid TagAddForm tagAddForm){
        System.out.println("添加标签");
    }
}

测试

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestValidate {
    @Autowired
    private UserValidate userValidate;


    @Test
    public void test2(){
         //new一个 TagAddForm对象作为参数并没有为其赋值
        TagAddForm tagAddForm=new TagAddForm();
        userValidate.add(tagAddForm);
    }
}

结果

spring boot 请求信息记录 spring boot 请求参数_spring_04

让自己变得更优秀才可以有更多资源