Springboot参数校验

validator内置注解

注解详细信息
@Null被注释的元素必须为 null
@NotNull被注释的元素必须不为 null
@AssertTrue被注释的元素必须为 true
@AssertFalse被注释的元素必须为 false
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Pattern(value)被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的 constraint

注解详细信息
@Email被注释的元素必须是电子邮箱地址
@Length被注释的字符串的大小必须在指定的范围内
@NotEmpty被注释的字符串的必须非空
@Range被注释的元素必须在合适的范围内
@NotBlank验证字符串非null,且长度必须大于0

注意

  • @NotNull 适用于任何类型被注解的元素必须不能与NULL
  • @NotEmpty 适用于集合或者数组不能为Null且长度必须大于0
  • @NotBlank 只能用于String上面 不能为null,调用trim()后,长度必须大于0

这里写一个手机号的正则校验(这只是一个简单手机号正则校验)—建议如果想实现复杂的参数校验可以自定义的约束(后面会写)

/**
 * 手机号
 */
@NotNull(message = "手机号不能为空")
@NotBlank(message = "手机号不能为空")
@Pattern(regexp ="^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String mobileNo;

@Validated

这个注解来自 package org.springframework.validation.annotation 包下

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})

可以 使用在类上、方法上、参数上

若要实现单一参数的效验

(我感觉若是使用restful风格,这个好像没用,因为若不传递参数会直接报错)

@Validated注解必须设置在类上

栗子:(单个参数异常处理也配置在了全局异常处理里面了)

 /**
     * 根据id查询用户
     * @param id
     * @return
     */
    @GetMapping("/find")
    @ApiOperation("查找用户")
    public User find(@NotBlank(message = "id不能为空") String id){
        User user = new User();
        user.setId(Long.parseLong(id));
        return user;
    }

若要实现javabean对象效验

@validated注解必须设置在参数前面

栗子:

/**
 * 添加商品
 * @param cartDTO
 */
@PostMapping
@ApiOperation("向购物车中添加商品")
public Integer addCart(@RequestBody @Validated CartDTO cartDTO){
    return cartService.addCart(cartDTO);
}

我这里json传递的是 {}

响应给我的json是(若想让响应结果像我这样还需要设置全局异常处理后面会说)

{
  "code": 1002,
  "msg": "参数效验失败",
  "data": [
    "商品id不能为空",
    "商品类型不能为空",
    "商品属性id不能为空",
    "用户id不能为空"
  ]
@Data
public class CartDTO {

    @ApiModelProperty(value = "购物车表ID")
    @NotNull(message = "更新时字段id必填",groups = {Update.class})
    private Long id;

    @ApiModelProperty(value = "用户ID")
    @NotNull(message = "用户id不能为空")
    private Long uid;

    @ApiModelProperty(value = "类型")
    @NotBlank(message = "商品类型不能为空")
    private String type;

    @ApiModelProperty(value = "商品ID")
    @NotNull(message = "商品id不能为空")
    private Long productId;

    @ApiModelProperty(value = "商品属性")
    @NotBlank(message = "商品属性id不能为空")
    private String productAttrUnique;

    @ApiModelProperty(value = "商品数量")
    @Min(message = "商品数量不能小于0",value = 0)
    private Integer cartNum;

    @ApiModelProperty(value = "0 = 未购买 1 = 已购买")
    private Boolean isPay;

    @ApiModelProperty(value = "是否为立即购买")
    private Boolean isNew;

    @ApiModelProperty(value = "拼团id")
    private Integer combinationId;

    @ApiModelProperty(value = "秒杀产品ID")
    private Integer seckillId;

    @ApiModelProperty(value = "砍价id")
    private Integer bargainId;
}

@Valid

来自 package javax.validation; 包下

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})

它可以使用在使用在类、字段、构造器、参数上

若要实现javabean对象效验和@Validated用法是一样的(这里就不举例子了)

@Validated和@Valid的区别

1 使用@Validated可以实现分组校验

栗子:

1、首先你要创建分组(我这里是创建了两个,新建验证、更新验证)而且必须要继承Default(javax.validation.groups.Default;)接口

package cn.newhopedairy.newshop.mall.common.cart.groups;

import javax.validation.groups.Default;

public interface Create extends Default {
}

package cn.newhopedairy.newshop.mall.common.cart.groups;

import javax.validation.groups.Default;

public interface Update extends Default {
}


2、在字段上配置分组属性(可以配置多个)

@Data
public class CartDTO {

  @ApiModelProperty(value = "购物车表ID")
  @NotNull(message = "更新时字段id必填",groups = {Update.class})
  @NotNull(message = "新增时字段id必填",groups = {Create.class})//这里我只是举个例子,实际中不会这样验证
  private Long id;
  ......
}

3、在验证的入口处(一般在controller的方法中)

@RestController
@Api("购物车接口")
@RequestMapping("/cart")
@Validated
public class CartController {
    /**
     * 添加商品
     * @param cartDTO
     */
    @PostMapping
    @ApiOperation("向购物车中添加商品")
    //
    public Integer addCart(@RequestBody @Validated(value = {Create.class}) CartDTO cartDTO){
        return cartService.addCart(cartDTO);
    }
    
        /**
     * 修改商品数量
     * @param cartDTO
     * @return
     */
    @PatchMapping
    @ApiOperation("修改购物车中的商品数据")
    public Integer updateCart(@RequestBody @Validated(Update.class) CartDTO cartDTO){
        return cartService.updateCart(cartDTO);
    }
}

4、测试

调用添加商品的方法

http://localhost:8080/cart

请求时的json数据为:

{
  "bargainId": 0,
  "cartNum": 0,
  "combinationId": 0,
  "isNew": true,
  "isPay": true,
  "productAttrUnique": "string",
  "productId": 0,
  "seckillId": 0,
  "type": "string",
  "uid": 0
}

响应的json数据为:

{
  "code": 1002,
  "msg": "参数效验失败",
  "data": [
    "新增时字段id必填"
  ]
}

调用修改商品的方法

http://localhost:8080/cart

请求时的json数据为:

{
  "bargainId": 0,
  "cartNum": 0,
  "combinationId": 0,
  "isNew": true,
  "isPay": true,
  "productAttrUnique": "string",
  "productId": 0,
  "seckillId": 0,
  "type": "string",
  "uid": 0
}

响应的json数据为:

{
  "code": 1002,
  "msg": "参数效验失败",
  "data": [
    "更新时字段id必填"
  ]
}

这样就是实现了分组效验的效果

“data”: [
“更新时字段id必填”
]

“data”: [
“新增时字段id必填”
]

2 @Validated和@Valid配合使用key实现嵌套验证

@Valid和@Valid配合使用也可以实现使用方式差不多。

@PostMapping
@ApiOperation("查找用户")
public User add(@RequestBody @Validated User user){

     return user;
}

@Data
@ApiModel(value="User对象", description="")
public class User implements Serializable {


    @ApiModelProperty(value = "年龄")
    @Min(value = 1,message = "年龄不能小于1")
    private Integer age;

    @ApiModelProperty(value = "邮箱")
    private String email;

    @Valid
    private Car car;
}

@Data
@ApiModel(value="User对象", description="")
public class Car {

    @NotBlank(message = "车的颜色不能为空")
    private String color;

    @Min(value = 5,message = "车长必须超过5米")
    private Integer type;
}

请求参数:
 {
  "age": 10,
  "car": {"color": "",
"type": 10},
  "email": "string",
  "id": 0,
  "name": "string"
  
}

响应参数:
{
  "code": 1002,
  "msg": "参数效验失败",
  "data": [
    "车的颜色不能为空"
  ]
}    


单参数校验

Springboot使用JSR-303 Validation进行验证,大部分博客都只提及对Controller中的dto对象进行属性校验。本文是介绍如何对Controller中的方法的单一参数进行校验,步骤如下。

1. Controller添加注解@Validated

@RestController
@RequestMapping("/test")
@Validated
public class TestController {}

2. 单一参数校验

使用@Min等标签校验参数。

    @PostMapping("/list")
    @ResponseBody
    public Result<String> getBookList(@Min(1) Integer pageNum,@Min(1) Integer pageSize, @RequestBody @Validated TestDto dto) {
        ...
    }

自定义异常

package com.example.hk_jsr303.exception;

import com.example.hk_jsr303.result.ResultVO;
import lombok.Getter;
import lombok.Setter;

/**
 * @Classname MyException
 * @Description TODO
 * @Date 2020/12/9 23:46
 * @Created by hekun
 */
@Getter
@Setter
public class MyException extends RuntimeException {
    private int code;
    private String msg;
    
    public MyException(){}
    
    public MyException(ResultVO resultVO) {
        this.code=resultVO.getCode();
        this.msg=resultVO.getMsg();
    }
}


定义全局异常处理

更具体的定义可以看一下这篇blog:
https://juejin.cn/post/6872226869605826573#heading-6

package com.example.hk_jsr303.exception;

import com.example.hk_jsr303.result.ResultVO;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @Classname GlobalExceptionHandler
 * @Description TODO
 * @Date 2020/12/9 15:11
 * @Created by hekun
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 方法参数错误异常
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<Object> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
        //log.error("方法参数错误异常");
        List<String> list=new ArrayList<>();
        // 从异常对象中拿到ObjectError对象
        if (!e.getBindingResult().getAllErrors().isEmpty()){
            for(ObjectError error:e.getBindingResult().getAllErrors()){
                list.add(error.getDefaultMessage().toString());
            }
        }
        // 然后提取错误提示信息进行返回
        return new ResultVO(1002,"参数效验失败",list);
    }

    /**
     * 单个参数异常处理
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public Object constraintViolationException(ConstraintViolationException ex) {

        // 获取具体的错误信息
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        // 打印数据
        violations.forEach(e -> System.out.println(e.getMessage()));

        return "单个-请求参数错误";
    }


}



//下面这个结果类要使用到
@Getter
@Setter
public class ResultVO<T> {
    /**
     * 状态码,比如1000代表响应成功
     */
    private int code;
    /**
     * 响应信息,用来说明响应情况
     */
    private String msg;
    /**
     * 响应的具体数据
     */
    private T data;


    public ResultVO(int code,String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    
    public ResultVO(int code,String msg) {
        this.code = code;
        this.msg = msg;
    }
}


自定义约束

一般情况内置的约束一般是够用了,如果有别的情况也是可以自己定义约束条件的

假设我们要给车设置一个颜色,而且颜色只能设置为red或者green

1、注意:@ListValue这个注解是我们自定义的

@Data
@ApiModel(value="User对象", description="")
public class Car {

    @NotBlank(message = "车的颜色不能为空")
    @ListValue(vals = {"red","green"},message = "车的颜色不对")
    private String color;
    
        @Min(value = 5,message = "车长必须超过5米")
    private Integer type;
}


2、我们要创建这个注解@ListValue,我们可以模仿官方写的注解比如@Min

package com.example.hk_jsr303.groups;


import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


@Constraint(validatedBy = {ListValueConstraintValidator.class})//自定义的约束校验器 ---很重要
//自定义的约束校验器 作用我们要在里面写一下校验规则 isValid() 方法返回true表示校验成功
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {

    //错误信息的提示
    String message() default "{com.sjl.common.valid.ListValue.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    //自定义值的类型
    String[] vals() default {};
}


3、写我们自己的约束校验器ListValueConstraintValidator ,这里要注意继承ConstraintValidator

package com.example.hk_jsr303.groups;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

/**
 * @Classname ListValueConstraintValidator
 * @Description TODO
 * @Date 2020/12/9 22:58
 * @Created by hekun
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,String> {

    private static Set<String> set = new HashSet<>();

    @Override
    public void initialize(ListValue constraintAnnotation) {
        for (String val : constraintAnnotation.vals()) {
            set.add(val);
        }
    }

    /**
     * 判断是否通过校验
     *
     * @param value   传入的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}


4、进行测试

/**
* 添加车辆
* @param car
* @return
*/
@PostMapping("/car")
@ApiOperation("新增车辆")
public Car addCar(@RequestBody @Validated Car car){
  return car;
}

请求json:
{
"color": "red1",
"type": 10
}

响应json:
{
"code": 1002,
"msg": "参数效验失败",
"data": [
  "车的颜色不对"
]
}


参考文档

Bean Validation 技术规范特性概述
)
https://www.jianshu.com/p/60aadd416eb4
https://juejin.cn/post/6844904118536912904#heading-7

https://juejin.cn/post/6844904016380461070#heading-6

https://juejin.cn/post/6872226869605826573#heading-7

https://juejin.cn/post/6844904133296652296#heading-6

https://www.cnblogs.com/songjilong/p/12663564.html

https://blog.csdn.net/justry_deng/article/details/86571671?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-8.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-8.control
————————————————

原文链接:xiaoxiaokunkunkun - Springboot参数校验

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值