0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SpringBoot Web应用如何进行参数校验?(上)

jf_78858299 来源:JAVA旭阳 作者:JAVA旭阳 2023-05-11 10:31 次阅读

在日常的接口开发中,为了防止非法参数对业务造成影响,经常需要对接口的参数做校验,例如登录的时候需要校验用户名密码是否为空,创建用户的时候需要校验邮件、手机号码格式是否准确。靠代码对接口参数一个个校验的话就太繁琐了,代码可读性极差。Validator框架就是为了解决开发人员在开发的时候少写代码,提升开发效率;Validator专门用来进行接口参数校验,例如常见的必填校验,email格式校验,用户名必须位于6到12之间 等等…

1. 集成Validator校验框架

1.1. 引入依赖包

<dependency>
  <groupId>org.springframework.boot<span class="hljs-name"groupId>
  <artifactId>spring-boot-starter-web<span class="hljs-name"artifactId>
<span class="hljs-name"dependency>

<dependency>
  <groupId>org.springframework.boot<span class="hljs-name"groupId>
  <artifactId>spring-boot-starter-validation<span class="hljs-name"artifactId>
<span class="hljs-name"dependency>

注:从springboot-2.3开始,校验包被独立成了一个starter组件,所以需要引入validation和web,而springboot-2.3之前的版本只需要引入 web 依赖就可以了。

注解 功能
@AssertFalse 可以为null,如果不为null的话必须为false
@AssertTrue 可以为null,如果不为null的话必须为true
@DecimalMax 设置不能超过最大值
@DecimalMin 设置不能超过最小值
@Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
@Future 日期必须在当前日期的未来
@Past 日期必须在当前日期的过去
@Max 最大不得超过此最大值
@Min 最大不得小于此最小值
@NotNull 不能为null,可以是空
@Null 必须为null
@Pattern 必须满足指定的正则表达式
@Size 集合、数组、map等的size()值必须在指定范围内
@Email 必须是email格式
@Length 长度必须在指定范围内
@NotBlank 字符串不能为null,字符串trim()后也不能等于“”
@NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
@Range 值必须在指定范围内
@URL 必须是一个URL

注:此表格只是简单的对注解功能的说明,并没有对每一个注解的属性进行说明;可详见源码。

1.2. 定义要参数校验的实体类

@Data
public class ValidVO {
    private String id;

    @Length(min = 6,max = 12,message = "appId长度必须位于6到12之间")
    private String appId;

    @NotBlank(message = "名字为必填项")
    private String name;

    @Email(message = "请填写正确的邮箱地址")
    private String email;

    private String sex;

    @NotEmpty(message = "级别不能为空")
    private String level;
}

在实际开发中对于需要校验的字段都需要设置对应的业务提示,即message属性。

1.3. 定义校验类进行测试

@RestController
@Slf4j
@Validated
public class ValidController {

    @ApiOperation("RequestBody校验")
    @PostMapping("/valid/test1")   
    public String test1(@Validated @RequestBody ValidVO validVO){
        log.info("validEntity is {}", validVO);
        return "test1 valid success";
    }

    @ApiOperation("Form校验")
    @PostMapping(value = "/valid/test2")
    public String test2(@Validated ValidVO validVO){
        log.info("validEntity is {}", validVO);
        return "test2 valid success";
    }

    @ApiOperation("单参数校验")
    @PostMapping(value = "/valid/test3")
    public String test3(@Email String email){
        log.info("email is {}", email);
        return "email valid success";
    }
}

这里我们先定义三个方法test1test2test3

  • test1使用了@RequestBody注解,用于接受前端发送的json数据,
  • test2模拟表单提交
  • test3模拟单参数提交

注意,当使用单参数校验时需要在Controller上加上@Validated注解,否则不生效。

1.4. 测试结果

  • test1的测试结果

发送值

POST http://localhost:8080/valid/test1
Content-Type: application/json

{
  "id": 1,
  "level": "12",
  "email": "47693899",
  "appId": "ab1c"
}

返回值提示的是org.springframework.web.bind.MethodArgumentNotValidException异常

{
  "status": 500,
  "message": "Validation failed for argument [0] in public java.lang.String com.jianzh5.blog.valid.ValidController.test1(com.jianzh5.blog.valid.ValidVO) with 3 errors: [Field error in object 'validVO' on field 'email': rejected value [47693899]; codes [Email.validVO.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [validVO.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@26139123,.*]; default message [不是一个合法的电子邮件地址]]...",
  "data": null,
  "timestamp": 1628239624332
}
复制代码
  • test2的测试结果

发送值

POST http://localhost:8080/valid/test2
Content-Type: application/x-www-form-urlencoded

id=1&level=12&email=476938977&appId=ab1c

返回值提示的是org.springframework.validation.BindException异常

{
  "status": 500,
  "message": "org.springframework.validation.BeanPropertyBindingResult: 3 errors\\nField error in object 'validVO' on field 'name': rejected value [null]; codes [NotBlank.validVO.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [validVO.name,name]; arguments []; default message [name]]; default message [名字为必填项]...",
  "data": null,
  "timestamp": 1628239301951
}
  • test3的测试结果

发送值

POST http://localhost:8080/valid/test3
Content-Type: application/x-www-form-urlencoded

email=476938977

返回值提示的是javax.validation.ConstraintViolationException异常

{
  "status": 500,
  "message": "test3.email: 不是一个合法的电子邮件地址",
  "data": null,
  "timestamp": 1628239281022
}

1.5. 问题

虽然我们之前定义了全局异常拦截器,也看到了拦截器确实生效了,但是Validator校验框架返回的错误提示太臃肿了,不便于阅读,为了方便前端提示,我们需要将其简化一下。

通过将参数异常加入全局异常来解决

1.6. 将参数异常加入全局异常

添加异常全局处理器RestExceptionHandler,单独拦截参数校验的三个异常,统一处理:

  • javax.validation.ConstraintViolationException
  • org.springframework.validation.BindException
  • org.springframework.web.bind.MethodArgumentNotValidException
@Slf4j
@RestControllerAdvice
public class RestExceptionHandler {
    /**
* 默认全局异常处理。
* @param e the e
* @return ResultData
*/
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultData exception(Exception e) {
        log.error("全局异常信息 ex={}", e.getMessage(), e);
        return ResultData.fail(ReturnCode.RC500.getCode(),e.getMessage());
    }

    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
    public ResponseEntity

1.7. 新的测试结果

  • test1测试结果

发送值

POST http://localhost:8080/valid/test1
Content-Type: application/json

{
  "id": 1,
  "level": "12",
  "email": "47693899",
  "appId": "ab1c"
}

接收值

{
  "status": 400,
  "message": "名字为必填项; 不是一个合法的电子邮件地址; appId长度必须位于6到12之间",
  "data": null,
  "timestamp": 1628435116680
}

2. 自定义注解

虽然Spring Validation 提供的注解基本上够用,但是面对复杂的定义,我们还是需要自己定义相关注解来实现自动校验。 比如上面实体类中的sex性别属性,只允许前端传递传 M,F 这2个枚举值,如何实现呢?

2.1. 第一步,创建自定义注解

可以根据Validator框架定义好的注解来仿写,基本上一致。

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(EnumString.List.class)
@Documented
@Constraint(validatedBy = EnumStringValidator.class)//标明由哪个类执行校验逻辑
public @interface EnumString {
    String message() default "value not in enum values.";

    Class?[] groups() default {};

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

    /**
* @return date must in this value array
*/
    String[] value();

    /**
* Defines several {@link EnumString} annotations on the same element.
*
* @see EnumString
*/
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
        @Retention(RUNTIME)
        @Documented
        @interface List {

            EnumString[] value();
        }
}

2.2. 第二步,自定义校验逻辑

定义处理逻辑EnumStringValidator

public class EnumStringValidator implements ConstraintValidator<EnumString, String> {
    private List<String> enumStringList;

    @Override
    public void initialize(EnumString constraintAnnotation) {
        enumStringList = Arrays.asList(constraintAnnotation.value());
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(value == null){
            return true;
        }
        return enumStringList.contains(value);
    }
}

2.3. 第三步,在字段上增加注解

@ApiModelProperty(value = "性别")
@EnumString(value = {"F","M"}, message="性别只允许为F或M")
private String sex;

2.4. 第四步,体验效果

发送值

POST http://localhost:8080/valid/test2
Content-Type: application/x-www-form-urlencoded

id=1&name=javadaily&level=12&email=476938977@qq.com&appId=ab1cdddd&sex=N

返回值

{
  "status": 400,
  "message": "性别只允许为F或M",
  "data": null,
  "timestamp": 1628435243723
}
复制代码
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    7649

    浏览量

    148542
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66820
  • spring
    +关注

    关注

    0

    文章

    333

    浏览量

    14161
  • Validator验
    +关注

    关注

    0

    文章

    3

    浏览量

    5761
  • Boot
    +关注

    关注

    0

    文章

    142

    浏览量

    35259
  • SpringBoot
    +关注

    关注

    0

    文章

    172

    浏览量

    106
收藏 人收藏

    评论

    相关推荐

    SpringBoot中的Druid介绍

    SpringBoot中Druid数据源配置
    发表于 05-07 09:21

    SpringBoot知识总结

    SpringBoot干货学习总结
    发表于 08-01 10:40

    怎么学习SpringBoot

    SpringBoot学习之路(X5)- 整合JPA
    发表于 06-10 14:52

    请问ATmega88如何进行电复位?

    ATmega88如何进行电复位?
    发表于 11-16 07:08

    对MCU某些数据进行CRC32校验

    关于STM32F4xx的硬件CRC32校验一、概述前段时间由于项目所需,要对MCU某些数据进行CRC32校验,MCU选用的是STM32F4系列,以前看到过STM32有硬件CRC32
    发表于 08-11 06:52

    变频器的参数何进行设置呢

    变频器的参数有数百甚至上千个,对这些参数进行合理正确的设置是使变频器高效运行并且满足用户要求的前提,那么,如何进行设置呢?本文以西门子MicroMaster440变频器为例
    发表于 09-03 07:43

    怎样去使用springboot

    怎样去使用springboot呢?学习springboot需要懂得哪些?
    发表于 10-25 07:13

    SpringBoot应用启动运行run方法

    )、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建**AnnotationConfigEmbeddedWebApplicationContext**,否则
    发表于 12-20 06:16

    Springboot是如何获取自定义异常并进行返回的

    这里看到新服务是封装的自定义异常,准备入手剖析一下,自定义的异常是如何进行抓住我们请求的方法的异常,并进行封装返回到。废话不多说,先看看如何才能实现封装异常,先来一个示例:在这里,您会看到新服务是一
    发表于 03-22 14:15

    何进行OPCDCOM配置

    何进行OPCDCOM配置(四会理士电源技术有限公司招聘)-如何进行OPCDCOM配置                      
    发表于 09-18 14:23 11次下载
    如<b class='flag-5'>何进行</b>OPCDCOM配置

    何进行电源设计 - 第1部分

    何进行电源设计 - 第1部分
    发表于 11-02 08:16 1次下载
    如<b class='flag-5'>何进行</b>电源设计 - 第1部分

    如何用责任链默认优雅地进行参数校验

    那么有什么更好的参数校验的方式呢?本文就推荐一种通过责任链设计模式来优雅地实现参数校验功能,我们通过一个用户注册的例子来讲明白如何实现。
    的头像 发表于 04-06 15:00 321次阅读

    什么是 SpringBoot

    本文从为什么要有 `SpringBoot`,以及 `SpringBoot` 到底方便在哪里开始入手,逐步分析了 `SpringBoot` 自动装配的原理,最后手写了一个简单的 `start` 组件,通过实战来体会了 `
    的头像 发表于 04-07 11:28 1037次阅读
    什么是 <b class='flag-5'>SpringBoot</b>?

    SpringBoot Web应用如何进行参数校验?(下)

    。靠代码对接口参数一个个校验的话就太繁琐了,代码可读性极差。Validator框架就是为了解决开发人员在开发的时候少写代码,提升开发效率;Validator专门用来进行接口参数
    的头像 发表于 05-11 10:37 335次阅读

    javaweb和springboot能一起用吗

    框架来开发 Web 应用程序。 首先,让我们了解一下 JavaWeb 和 SpringBoot 的基本概念。 JavaWeb 是一种用于开发基于 Java 技术的 Web 应用程序的技术框架。它提供
    的头像 发表于 11-16 10:54 924次阅读