SpringMVC 做后端验证

SpringMVC 做后端验证

什么叫后端验证

针对前后端分离的项目, VUE端处理好前端校验后,后端也要对必填字段进行控制,确保整个系统的健壮性!

简单方式校验

使用Google 提供的Guava工具包进行校验

请参考文章-> Google工具包Guava——代码校验Preconditions - 跳转链接

使用实体类属性加注解的方式进行校验

应用场景

DTO或者实体类在使用RequestBody传参的时候, 方法体在根据参数进行逻辑处理时,尝尝需要对参数进行校验,一些简单的校验可以使用Validator校验方法。

常用注解

@Null    限制必须为null
@NotNull    限制必须不能为null
@AssertFalse    限制必须为false
@AssertTrue    限制必须为true
@DecimalMax(value)    限制必须为一个不超过指定大小值的数字
@DecimalMin(value)    限制必须为一个不低于指定大小值的数字
@Digits(integert,fraction)    限制必须为一个小数,且整数部分位数不能超过integer,小数部分位数不能超过fraction
@Futrue    限制必须为一个将来的日期
@Past    限制必须为一个过去的日期
@Max(value)    限制必须为一个不超过指定大小值的数字
@Min(value)    限制必须为一个不低于指定大小值的数字
@Pattern(value)    限制必须符合指定的正则表达式
@Size(max,min)    限制字符长度必须处在max和min的值之间
@NotEmpty    验证注解的元素值不为null且不为空(字符串长度不为0,集合大小不为0)
@NotBlank    验证注解的元素值不为空(不为null,且去除首位空格后长度不为0),不同于@NotEmpty,@NotBlank只适用于字符串的校验且在校验时会去除字符串的空格
@Email    验证值是否为邮箱,也可以通过正则表达式和flag指定自定义的email格式
@Length    限制字符串参数长度
@Range    限制数值类型或者字符串在指定范围
@URL    对URL地址进行验证

非空校验注解 @NotNull、@NotEmpty、@NotBlank的区别和运用

@NotNull 常用在数据类型为Long,Integer的基础数据类型上,可以配合@Size、@Max、@Min对数值进行大小的控制。

@ApiModelProperty(value = "编号")
@NotNull(message = "编号不能为空")
@JsonFormat(shape = Shape.STRING)
private Long id;

@ApiModelProperty(value = "价格")
@NotNull(message = "价格不能为空")
@Min(value = 100, message = "价格不能低于100")
@Max(value = 5000, message = "价格不能超过5000")
private Integer price;

@ApiModelProperty(value = "数量")
@NotNull(message = "数量不能为空")
@Size(min = 10, max = 50, message = "数量必须不低于10不超过50")
private Integer num;

@NotEmpty 该注解只能应用于char可读序列(可简单理解为String对象),colleaction,map,array上,因为该注解要求的是对象不为null且size>0,所以只有上述对象是拥有size属性的,而Integer,Long等基础对象包装类没有该属性。 常用在集合类和数组类型上,也可以配合@Size进行大小的控制。

@ApiModelProperty(value = "材料保存列表", required = true)
@NotEmpty(message = "存货清单列表不能为空")
@Size(min = 1, message = "至少需要一条数据")
private List<MaterialAcceptanceItemsSaveVo> itemsSaveList;

@NotBlank 只用于String数据类型上,可以和@Len配合使用限制字符长度。


@ApiModelProperty(value = "供应商", required = true)
@NotBlank(message = "供应商不能为空")
@Len(min = 1, max = 500)
private String provider;
 
@ApiModelProperty(value = "发票号", required = true)
@Len(min = 0, max = 250)
private String invoiceNo;

使用示例

  1. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

  1. 实体类

package com.example.demo.domain;
 
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ContentFontStyle;
import lombok.Data;
 
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.util.Date;
 
/**
 * @author linaibo
 * @version 1.0
 * Create by 2022/12/4 14:13
 */
@Data
@ExcelIgnoreUnannotated
public class Teacher {
 
    //id
//    @ExcelProperty(value = "id")
    @NotNull(message = "id不能为空")
    private int id;
    //名字
    @ContentFontStyle(color = 10,fontHeightInPoints=12)
    @ExcelProperty(value = "名字")
    @Pattern(regexp = "1|2", message = "只能填写1或者2中的一个")
    @NotBlank(message = "名字不能为空")
    private String name;
    //邮箱
    @ExcelProperty(value = "邮箱")
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    //年龄
    @ExcelProperty(value = "年龄")
    @NotNull(message = "年龄不能为空")
    @Max(value = 20,message = "必须小于20")
    private int age;
    //是否结婚
    @ExcelProperty(value = "是否结婚")
    @AssertTrue(message = "是否结婚必须是是")
    private boolean isMarry;
    //是否有孩子
    @ExcelProperty(value = "是否有孩子")
    @AssertFalse(message = "是否结婚必须是否")
    private boolean isChilden;
    //体重
    @ExcelProperty(value = "体重")
    @NotNull(message = "体重不能为空")
    @DecimalMax(value = "100", message = "体重不能超过100")
    private BigDecimal weight;
    //身高
    @ExcelProperty(value = "身高")
    @NotNull(message = "身高不能为空")
    @DecimalMin(value = "170", message = "身高不能低于170")
    private BigDecimal height;
    //出生日期
    @ExcelProperty(value = "出生日期")
    @NotNull(message = "出生日期不能为空")
    @Past(message = "出生日期必须为过去的日期")
    private Date date1;
    //死亡日期
    @ExcelProperty(value = "死亡日期")
    @NotNull(message = "死亡日期不能为空")
    @Future(message = "死亡日期必须为将来的日期")
    private Date date2;
 
 
}

  1. 校验逻辑

3.1 校验逻辑 Service 层


package com.example.demo.service.impl;
 
import cn.hutool.core.date.DateUtil;
import com.example.demo.domain.Teacher;
import com.example.demo.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.util.Set;
 
/**
 * @author linaibo
 * @version 1.0
 * Create by 2022/12/4 14:42
 */
@Service
public class TeacherServiceImpl implements TeacherService {
 
    @Autowired
    private Validator validator;
 
    public void check() {
        Teacher teacher = new Teacher();
        teacher.setId(12);
        teacher.setAge(25);
        teacher.setDate1(DateUtil.parse("2023-11-01","yyyy-MM-dd"));
        teacher.setDate2(DateUtil.parse("2020-11-12","yyyy-MM-dd"));
        teacher.setEmail("1245783");
        teacher.setChilden(true);
        teacher.setMarry(false);
        teacher.setHeight(new BigDecimal(160));
        teacher.setWeight(new BigDecimal(150));
        teacher.setName("小琳");
//        teacher.setId(12);
//        teacher.setAge(25);
//        teacher.setDate1(DateUtil.parse("2020-11-01","yyyy-MM-dd"));
//        teacher.setDate2(DateUtil.parse("2023-11-12","yyyy-MM-dd"));
//        teacher.setEmail("1245783@qq.com");
//        teacher.setChilden(false);
//        teacher.setMarry(true);
//        teacher.setHeight(new BigDecimal(170));
//        teacher.setWeight(new BigDecimal(100));
//        teacher.setName("小琳");
        System.out.println(teacher);
        Set<ConstraintViolation<Teacher>> validate = validator.validate(teacher);
//        validate.forEach(item -> System.out.println(item.getMessage()));
        System.out.println(validate);
        throw new ConstraintViolationException(validate);
    }
 
}

校验的结果得到一个set集合,一般是抛出异常,使用ConstraintViolationException(validate)直接对结果集进行抛出,结果如下

javax.validation.ConstraintViolationException: isChilden: 是否结婚必须是否, isMarry: 是否结婚必须是是, height: 身高不能低于170, date2: 死亡日期必须为将来的日期, email: 邮箱格式不正确, date1: 出生日期必须为过去的日期, weight: 体重不能超过100

也可以循环取得具体的信息,拼接之后直接抛出。

3.2 校验逻辑 Controller 层


package com.example.demo.service.impl;
 
import cn.hutool.core.date.DateUtil;
import com.example.demo.domain.Teacher;
import com.example.demo.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.util.Set;
 
/**
 * @author linaibo
 * @version 1.0
 * Create by 2022/12/4 14:42
 */
@Service
public class TeacherController {
 
    @Autowired
    private Validator validator;
    
    @PostMapping(value = "/validate")
    public void check(@RequestBody @Validated Teacher teacher) {
        Teacher teacher = new Teacher();
        teacher.setId(12);
        teacher.setAge(25);
        teacher.setDate1(DateUtil.parse("2023-11-01","yyyy-MM-dd"));
        teacher.setDate2(DateUtil.parse("2020-11-12","yyyy-MM-dd"));
        teacher.setEmail("1245783");
        teacher.setChilden(true);
        teacher.setMarry(false);
        teacher.setHeight(new BigDecimal(160));
        teacher.setWeight(new BigDecimal(150));
        teacher.setName("小琳");
//        teacher.setId(12);
//        teacher.setAge(25);
//        teacher.setDate1(DateUtil.parse("2020-11-01","yyyy-MM-dd"));
//        teacher.setDate2(DateUtil.parse("2023-11-12","yyyy-MM-dd"));
//        teacher.setEmail("1245783@qq.com");
//        teacher.setChilden(false);
//        teacher.setMarry(true);
//        teacher.setHeight(new BigDecimal(170));
//        teacher.setWeight(new BigDecimal(100));
//        teacher.setName("小琳");
        System.out.println(teacher);
        Set<ConstraintViolation<Teacher>> validate = validator.validate(teacher);
//        validate.forEach(item -> System.out.println(item.getMessage()));
        System.out.println(validate);
        throw new ConstraintViolationException(validate);
    }
 
}

示例 1


@Autowired
private Validator validator;
 
public StorageFee calculateBasicStorageFee(StorageChargeDto dto) {
    log.info("calculateBasicStorageFee入参信息,dto={}", JSON.toJSONString(dto));
    StorageFee fee = new StorageFee();

    //校验参数是否为空
    BeanValidators.validateWithException(validator, dto);
    //不含税费率计算
    BigDecimal expenseAmout = getAmt(dto.getExpenseAmt(), dto);
    //含税梯级费率计算
    BigDecimal expenseTaxAmout = getAmt(dto.getExpenseTaxAmt(), dto);

    fee.setExpenseTotalAmout(expenseAmout);
    fee.setExpenseTotalTaxAmout(expenseTaxAmout);
    return fee;
}

使用到的工具类

package com.ruoyi.common.core.utils.bean;
 
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
 
/**
 * bean对象属性验证
 * 
 * @author ruoyi
 */
public class BeanValidators
{
    public static void validateWithException(Validator validator, Object object, Class<?>... groups)
            throws ConstraintViolationException
    {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty())
        {
            throw new ConstraintViolationException(constraintViolations);
        }
    }
}

示例 2


Set<ConstraintViolation<BizEntryOrderDetail>> validate = validator.validate(goods);
if (!validate.isEmpty()) {
    return AjaxResult.error("入库单导入失败:" + validate.iterator().next().getMessage());
}

Last Updated 10/25/2025, 5:44:13 AM