如何实现 Java SpringBoot 自动验证入参数据的有效性

简介: 如何实现 Java SpringBoot 自动验证入参数据的有效性

Java SpringBoot 通过javax.validation.constraints下的注解,实现入参数据自动验证

如果碰到 @NotEmpty 否则不生效,注意看下 @RequestBody 前面是否加上了@Valid

Validation常用注解汇总

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

示例

/**
   * 用户名
   */
  @NotBlank(message = "用户名不能为空")
  private String username;
  /**
   * 用户真实姓名
   */
  @NotBlank(message = "用户真实姓名不能为空")
  private String name;
  /**
   * 密码
   */
  @Pattern(regexp = "^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[^A-Za-z0-9]))(?=^[^\\u4e00-\\u9fa5]{0,}$).{8,20}$", message = "密码过于简单有被盗风险,请保证密码大于8位,并且由大小写字母、数字,特殊符号组成")  
  private String password;
  /**
   * 邮箱
   */
  @NotBlank(message = "邮箱不能为空")
  @Email(message = "邮箱格式不正确")
  private String email;
  /**
   * 手机号
   */
  @NotBlank(message = "手机号不能为空")
  @Pattern(regexp = "^(1[0-9])\\d{9}$", message = "手机号格式不正确")
  private String mobile;

Demo

入参对象上,添加注解及说明

package com.vipsoft.web.entity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;
/**
 * 定时任务调度
 */
public class QuartzJob implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 任务序号
     */
    private long jobId;
    /**
     * 任务名称
     */
    @NotBlank(message = "任务名称不能为空")
    @Size(max = 10, message = "任务名称不能超过10个字符")
    private String jobName;
    /**
     * 任务组名
     */
    @NotBlank(message = "任务组名不能为空")
    @Size(max = 10, message = "任务组名不能超过10个字符")
    private String jobGroup;
    /**
     * 调用目标字符串
     */
    private String invokeTarget;
    /**
     * 执行表达式
     */
    private String cronExpression;
    /**
     * cron计划策略 0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行
     */
    private String misfirePolicy = "0";
    /**
     * 并发执行 0=允许,1=禁止
     */
    private String concurrent;
    /**
     * 任务状态(0正常 1暂停)
     */
    private String status;
    /**
     * 备注
     */
    private String remark;
}

Controller @RequestBody 前面必须加上 @Valid 否则不生效

import javax.validation.Valid;
@RestController
@RequestMapping("schedule")
public class ScheduleController {
    private Logger logger = LoggerFactory.getLogger(ScheduleController.class);
  
    @RequestMapping("/add")
    public ApiResult addTask(@Valid @RequestBody QuartzJob param) throws Exception {
        logger.info("添加调度任务 => {} ", JSONUtil.toJsonStr(param));
        
        return new ApiResult("添加成功");
    }
}

异常处理,统一返回对象,方便前端解析

GlobalExceptionHandler

package com.vipsoft.web.exception;
import cn.hutool.core.util.StrUtil;
import com.vipsoft.web.utils.ApiResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
/**
 * 全局异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    /**
     * 处理自定义异常
     */
    @ExceptionHandler(CustomException.class)
    public ApiResult handleException(CustomException e) {
        // 打印异常信息
        logger.error("### 异常信息:{} ###", e.getMessage());
        return new ApiResult(e.getCode(), e.getMessage());
    }
    /**
     * 参数错误异常
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public ApiResult handleException(Exception e) {
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException validException = (MethodArgumentNotValidException) e;
            BindingResult result = validException.getBindingResult();
            StringBuffer errorMsg = new StringBuffer();
            if (result.hasErrors()) {
                List<ObjectError> errors = result.getAllErrors();
                errors.forEach(p -> {
                    FieldError fieldError = (FieldError) p;
                    errorMsg.append(fieldError.getDefaultMessage()).append(",");
                    logger.error("### 请求参数错误:{" + fieldError.getObjectName() + "},field{" + fieldError.getField() + "},errorMessage{" + fieldError.getDefaultMessage() + "}");
                });
                return new ApiResult(6001, errorMsg.toString());
            }
        } else if (e instanceof BindException) {
            BindException bindException = (BindException) e;
            if (bindException.hasErrors()) {
                logger.error("### 请求参数错误: {}", bindException.getAllErrors());
            }
        }
        return new ApiResult(6001, "参数无效");
    }
    /**
     * 处理HttpRequestMethodNotSupporte异常
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Object methodHandler(HttpRequestMethodNotSupportedException e) {
        // 打印异常信息
        logger.error("### 异常信息:{} ###", e.getMessage());
        return new ApiResult(6000, e.getMessage());
    }
    /**
     * 处理所有不可知的异常
     */
    @ExceptionHandler(Exception.class)
    public ApiResult handleOtherException(Exception e) {
        // 打印异常信息
        logger.error("### 系统内部错误:{} ###", e.getMessage(), e);
        String warnMsg = StrUtil.isEmpty(e.getMessage()) ? "### 系统内部错误 ###" : e.getMessage();
        return new ApiResult(6000, "系统内部错误", e.getMessage());
    }
}

统一返回对像 ApiResult

package com.vipsoft.web.utils;
//import com.github.pagehelper.PageInfo;
import java.io.Serializable;
public class ApiResult implements Serializable {
    /**
     * 返回编码 0:失败、1:成功
     */
    private int code;
    /**
     * 返回消息
     */
    private String message;
    /**
     * 返回对象
     */
    private Object data;
    /**
     * 分页对象
     */
    private Page page;
    public ApiResult() {
        this.code = 1;
        this.message = "请求成功";
    }
    public ApiResult(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
    public ApiResult(Integer code, String message, Object data) {
        this.code = code;
        this.message = message;
        this.setData(data);
    }
    public ApiResult(Object data) {
        this.code = 1;
        this.message = "请求成功";
        this.setData(data);
    }
//    public ApiResult(PageInfo pageInfo) {
//        this.code = 1;
//        this.message = "请求成功";
//        this.setData(pageInfo.getList());
//        this.setPage(convertToPage(pageInfo));
//    }
//
//    public Page convertToPage(PageInfo pageInfo) {
//        Page result = new Page();
//        result.setTotalCount(pageInfo.getTotal());
//        result.setPageNum(pageInfo.getPageNum());
//        result.setPageCount(pageInfo.getPages());
//        result.setPageSize(pageInfo.getPageSize());
//        result.setPrePage(pageInfo.getPrePage());
//        result.setNextPage(pageInfo.getNextPage());
//        return result;
//    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public Page getPage() {
        return page;
    }
    public void setPage(Page page) {
        this.page = page;
    }
}

运行结果如下

目录
相关文章
|
18天前
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
|
2月前
|
JSON 安全 算法
|
2月前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
42 6
|
2月前
|
存储 Java API
深入剖析Java Map:不只是存储数据,更是设计艺术的体现!
【10月更文挑战第17天】在Java编程中,Map是一种重要的数据结构,用于存储键值对,并展现了设计艺术的精髓。本文深入剖析了Map的设计原理和使用技巧,包括基本概念、设计艺术(如哈希表与红黑树的空间时间权衡)、以及使用技巧(如选择合适的实现类、避免空指针异常等),帮助读者更好地理解和应用Map。
98 3
|
22天前
|
SQL 前端开发 关系型数据库
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
48 9
|
18天前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
42 2
|
18天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
25 2
|
23天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
40 2
|
27天前
|
监控 前端开发 Java
Java SpringBoot –性能分析与调优
Java SpringBoot –性能分析与调优
|
27天前
|
存储 easyexcel Java
SpringBoot+EasyExcel轻松实现300万数据快速导出!
本文介绍了在项目开发中使用Apache POI进行数据导入导出的常见问题及解决方案。首先比较了HSSFWorkbook、XSSFWorkbook和SXSSFWorkbook三种传统POI版本的优缺点,然后根据数据量大小推荐了合适的使用场景。接着重点介绍了如何使用EasyExcel处理超百万数据的导入导出,包括分批查询、分批写入Excel、分批插入数据库等技术细节。通过测试,300万数据的导出用时约2分15秒,导入用时约91秒,展示了高效的数据处理能力。最后总结了公司现有做法的不足,并提出了改进方向。