欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Springboot参数校验--最全

程序员文章站 2022-04-16 16:01:28
Springboot参数校验validator内置注解注解详细信息@Null被注释的元素必须为 null@NotNull被注释的元素必须不为 null@AssertTrue被注释的元素必须为 true@AssertFalse被注释的元素必须为 false@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值@DecimalMin(val...

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不能为空"
  ]
}

javabean对象是

@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": [
    "车的颜色不能为空"
  ]
}    

自定义异常

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);
    }
}

5、进行测试

/**
 * 添加车辆
 * @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": [
    "车的颜色不对"
  ]
}

参考文档

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

本文地址:https://blog.csdn.net/kunug/article/details/110944563

相关标签: java