使用传统方式的弊端

public String addUser(User user) {if (user == null || user.getId() == null || user.getAccount() == null || user.getPassword() == null || user.getEmail() == null) {return "对象或者对象字段不能为空";}if (StringUtils.isEmpty(user.getAccount()) || StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getEmail())) {return "不能输入空字符串";}if (user.getAccount().length() < 6 || user.getAccount().length() > 11) {return "账号长度必须是6-11个字符";}if (user.getPassword().length() < 6 || user.getPassword().length() > 16) {return "密码长度必须是6-16个字符";}if (!Pattern.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", user.getEmail())) {return "邮箱格式不正确";}// 参数校验完毕后这里就写上业务逻辑return "success";}

这样做确实没有什么问题,而且排版也工整,但代码太繁琐了,如果有几十个字段要校验,那这个方法里面将会变得非常臃肿,实在不够优雅。下面我们就来讲讲如何使用最优雅的方式来解决。

引入依赖

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

注解说明

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

下面我们以此来在业务中实现

一、对实体类进行校验

1、entity

@Data
public class User {@NotNull(message = "用户id不能为空")private Long id;@NotNull(message = "用户账号不能为空")@Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")private String account;@NotNull(message = "用户密码不能为空")@Size(min = 6, max = 11, message = "密码长度必须是6-16个字符")private String password;@NotNull(message = "用户邮箱不能为空")@Email(message = "邮箱格式不正确")private String email;
}

2、controller

@RestController
public class UserController {@PostMapping("/addUser")public void addUser(@RequestBody @Valid User user) {//业务}
}

3、编写全局统一异常处理

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.stream.Collectors;/*** 全局异常处理** @author master*/
@RestControllerAdvice
public class ExceptionConfig {/*** 参数为实体类* @param e* @return*/@ExceptionHandler(value = MethodArgumentNotValidException.class)public String handleValidException(MethodArgumentNotValidException e) {// 从异常对象中拿到ObjectError对象ObjectError objectError = e.getBindingResult().getAllErrors().get(0);// 然后提取错误提示信息进行返回return objectError.getDefaultMessage();}/*** 参数为单个参数或多个参数* @param e* @return*/@ExceptionHandler(value = ConstraintViolationException.class)public String handleConstraintViolationException(ConstraintViolationException e) {// 从异常对象中拿到ObjectError对象return e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.toList()).get(0);}}

然后我们使用apipost测试

二 针对单个参数进行校验

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import javax.validation.constraints.NotNull;@RestController
@Validated
public class TestController {@GetMapping("/test")public void test(@NotNull(message = "id不能为空") Integer id) {}
}

然后我们使用apipost测试

三 分组校验

场景:在新增时我们需要id为空,但修改时我们又需要id不为空,总不可能搞两个类吧,这时候分组校验的用处就来了

1、entity

import lombok.Data;import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;@Data
public class User {public interface Insert{}public interface Update{}@NotNull(message = "用户id不能为空",groups = Update.class)@Null(message = "用户id必须为空",groups = Integer.class)private Long id;private String account;private String password;private String email;
}

2、controller

@PostMapping("/add")
public void add(@RequestBody @Validated(User.Insert.class) User user) {}

添加时就用User.Insert.class,修改时就用User.Update.class

四、自定义分组校验

场景:当type为1时,需要参数a不为空,当type为2时,需要参数b不为空。

1、entity

import com.example.demo.provider.CustomSequenceProvider;
import lombok.Data;
import org.hibernate.validator.group.GroupSequenceProvider;import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;@Data
@GroupSequenceProvider(value = CustomSequenceProvider.class)
public class CustomGroup {/*** 类型*/@Pattern(regexp = "[A|B]" , message = "类型不必须为 A|B")private String type;/*** 参数A*/@NotEmpty(message = "参数A不能为空" , groups = {WhenTypeIsA.class})private String paramA;/*** 参数B*/@NotEmpty(message = "参数B不能为空", groups = {WhenTypeIsB.class})private String paramB;/*** 分组A*/public interface WhenTypeIsA {}/*** 分组B*/public interface WhenTypeIsB {}}

2、CustomSequenceProvider

import com.example.demo.controller.CustomGroup;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;import java.util.ArrayList;
import java.util.List;public class CustomSequenceProvider implements DefaultGroupSequenceProvider<CustomGroup> {@Overridepublic List<Class<?>> getValidationGroups(CustomGroup form) {List<Class<?>> defaultGroupSequence = new ArrayList<>();defaultGroupSequence.add(CustomGroup.class);if (form != null && "A".equals(form.getType())) {defaultGroupSequence.add(CustomGroup.WhenTypeIsA.class);}if (form != null && "B".equals(form.getType())) {defaultGroupSequence.add(CustomGroup.WhenTypeIsB.class);}return defaultGroupSequence;}
}

3、controller

@PostMapping("/add")
public void add(@RequestBody @Validated CustomGroup user) {}

五、自定义校验

虽然官方提供的校验注解已经满足很多情况了,但还是无法满足我们业务的所有需求,比如校验手机号码,下面我就以校验手机号码来做一个示例。

1、定义校验注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {String message() default "手机号码格式有误";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

注:groups和payload是必须要写的,Constraint是使用哪个类来进行校验。

2、实现注解

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;/*** @author master*/
public class PhoneValidator implements ConstraintValidator<Phone, Object> {@Overridepublic boolean isValid(Object telephone, ConstraintValidatorContext constraintValidatorContext) {String pattern = "^1[3|4|5|6|7|8|9]\\d{9}$";return Pattern.matches(pattern, telephone.toString());}
}

最后直接用到参数前面或者实体类变量上面即可。

六、嵌套校验

当某个对象中还包含了对象需要进行校验,这个时候我们需要用嵌套校验。

@Data
public class TestAA {@NotEmpty(message = "id不能为空")private String id;@NotNull@Validprivate Job job;@Datapublic class Job {@NotEmpty(message = "content不能为空")private String content;}
}

七、快速失败

Spring Validation默认会校验完所有字段,然后才抛出异常。可以通过配置,开启Fali Fast模式,一旦校验失败就立即返回。

import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;@Configuration
public class FailFastConfig {@Beanpublic Validator validator() {ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class).configure()// 快速失败模式.failFast(true).buildValidatorFactory();return validatorFactory.getValidator();}
}

注意事项

SpringBoot 2.3.x 移除了validation依赖需要手动引入依赖。

总结

非空校验是校验的第一步, 除了非空校验,我们还需要做到以下几点:

  • 普通参数 - 需要限定字段的长度。如果会将数据存入数据库,长度以数据库为准,反之根据业务确定。

  • 类型参数 - 最好使用正则对可能出现的类型做到严格校验。比如type的值是【0|1|2】这样的。

  • 列表(list)参数 - 不仅需要对list内的参数是否合格进行校验,还需要对list的size进行限制。比如说 100。

  • 日期,邮件,金额,URL这类参数都需要使用对于的正则进行校验。

  • 参数真实性 - 这个主要针对于 各种Id 比如说 userId、merchantId,对于这样的参数,都需要进行真实性校验

参数校验越严格越好,严格的校验规则不仅能减少接口出错的概率,同时还能避免出现脏数据,从而来保证系统的安全性和稳定性。

SpringBoot参数校验相关推荐

  1. Springboot 参数校验@Valid @Validated(最新最全)

    Springboot 参数校验@Valid @Validated(最新最全) 提示:统一参数校验,异常处理 文章目录 Springboot 参数校验@Valid @Validated(最新最全) 前言 ...

  2. springboot 参数校验详解

    https://www.jianshu.com/p/89a675b7c900 在日常开发写rest接口时,接口参数校验这一部分是必须的,但是如果全部用代码去做,显得十分麻烦,spring也提供了这部分 ...

  3. 补习系列(4)-springboot 参数校验详解

    目录 目标 一.PathVariable 校验 二.方法参数校验 三.表单对象校验 四.RequestBody 校验 五.自定义校验规则 六.异常拦截器 参考文档 目标 对于几种常见的入参方式,了解如 ...

  4. springboot参数校验validation

    文章目录 1. 引入 2. 基本使用 2.1 引入依赖 2.2 基本使用 2.3 注解介绍 2.4 返回值完善 2.5 统一异常处理 2.6 @Valid和@Validate注解 3. 分组校验 4. ...

  5. SpringBoot参数校验--List类型

        我们在写后台接口的时候,通常会定义DTO来接收参数,在DTO中使用注解书写验证的规则:然后在Controller层使用@validated注解来验证自己制定的校验规则.但当我们的接口接收的参数 ...

  6. springboot 参数校验

    1.场景 1)参数为实体类 web层需要对前端传输的数据做校验,如果按照下面的代码写,就太浪费时间了,并且代码看起来很乱. public AjaxResult queryUserProjectCont ...

  7. springboot参数校验,对象的某属性校验

    对于前端来的数据,后端难免要进行合法性校验,这里可以采用springboot自带的Validated注解来实现,详细方法如下: 实体类: public class User implements Se ...

  8. SpringBoot:参数校验的使用(validator)

    文章目录 SpringBoot参数校验的使用(validator) 一.validator简介 二.注解介绍 内置注解 扩展注解 三.validator的使用(手动校验) 创建校验工具类 对一个对象进 ...

  9. SpringBoot实现通用的接口参数校验

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:cipher juejin.im/post/5af3c25b ...

最新文章

  1. 基于点线特征避免单目视觉SLAM的退化
  2. LINUX CP命令
  3. Web生产:外部JS文件中的绝对URL?
  4. sae上部署第一个站
  5. ubuntu 16.04 mysql5.7.17 开放远程3306端口
  6. 无线覆盖项目地勘——无线地勘记录
  7. NOIP2011 聪明的质监员
  8. Java调用跟踪系统_Tracer:在分布式系统中的调用跟踪和日志相关
  9. ISP QoS Lab
  10. [Java] 蓝桥杯ADV-184 算法提高 素数求和
  11. POJ 2739 Sum of Consecutive Prime Numbers 难度:0
  12. latex 中的长度单位,尺寸
  13. [jzoj100047]【NOIP2017提高A组模拟7.14】基因变异
  14. (E1)ENVI-met介绍及下载
  15. aac格式怎么转换为MP3格式
  16. JWT,JWS与JWE区别
  17. b500k电位器引脚接法_电位器引脚含义是什么
  18. 每天进步一点点【稀疏数组】
  19. 机器视觉问题:工业普通定焦镜头如何计算景深?景深计算
  20. Android L中水波纹点击效果的实现

热门文章

  1. (附源码)springboot社区快递代取服务系统 毕业设计051434
  2. 模拟koa洋葱模型实现
  3. c语言 猜数字小游戏
  4. Python猜数字小游戏
  5. 沃伦巴菲特和约尔欧斯汀如何克服公共演讲的恐惧
  6. 【LeetCode刷题】重叠区间问题
  7. python fft 归一化_基本的FFT归一化问题
  8. 对偶量子计算机,斯坦福大学:通过时空对偶性探索量子纠缠的动力学
  9. 第一次写博客,给大家推荐几个C语言视频教程
  10. EVERYTHING本地搜索工具