栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Boot 参数校验的优雅实现

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Boot 参数校验的优雅实现

文章目录
  • Spring Validation概述
  • 1、基本使用
    • 1.1、首先以 新增用户信息 为示例来测试第一类
      • 1.1.1、正常测试
      • 1.1.2、长度测试
      • 1.1.3、@NotNull 测试
    • 1.2、第二类以 获取用户信息 @RequestParam / @PathVariable 测试
  • 2、进阶使用
    • 2.1、分组校验
    • 2.2、嵌套校验
    • 2.3 、集合测试
    • 2.4 、自定义校验
    • 2.5 编程式校验
  • 3、fail-fast
  • 4、更多注解

如果你有前端传参校验的需求,而又不想手动写if去校验的话,本文应该可以帮到你 :)

Spring Validation概述

Spring Validation是对hibernate validation的二次封装,用于支持spring mvc参数自动校验。

1、基本使用

如果spring-boot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。如果spring-boot版本大于2.3.x,则需手动导入依赖

版本说明:springboot 2.6.1

maven依赖


      org.springframework.boot
      spring-boot-starter-web
  
  
      org.hibernate
      hibernate-validator
      6.0.1.Final
  
  
      org.projectlombok
      lombok
      true
  

分两类参数传递进行测试

一般来说

1、POST、PUT请求,使用@RequestBody传递参数;

2、GET请求,使用@RequestParam / @PathVariable传递参数。

1.1、首先以 新增用户信息 为示例来测试第一类

新建dto

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotNull;


@Data
public class User {
    @NotNull
    @Length(min = 2, max = 10)
    private String username;

    @NotNull
    @Length(min = 2, max = 10)
    private String pwd;

    private String id;
}

新建接口测试

import com.example.springvalidation.dto.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {
    
    @PostMapping("save")
    public String saveUser(@RequestBody @Validated User user){
        return "validation success";
    }
}
1.1.1、正常测试

1.1.2、长度测试

或短或长皆会由spring转为400

同时控制台,会打印消息 “长度需要在2和10之间”

2021-12-17 13:26:13.950  WARN 19524 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.example.springvalidation.controller.TestController.saveUser(com.example.springvalidation.dto.User): [Field error in object 'user' on field 'username': rejected value [1]; codes [Length.user.username,Length.username,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.username,username]; arguments []; default message [username],10,2]; default message [长度需要在2和10之间]] ]
1.1.3、@NotNull 测试

由结果看, null会进行拦截,美中不足的是 ""会以长度不足的分支去拦截

自定义消息提示

@NotNull(message="null自定义提示")
@Length(min = 2, max = 10, message="长度自定义提示")

1.2、第二类以 获取用户信息 @RequestParam / @PathVariable 测试

新增接口,注意类上面要加@Validated注解

import com.example.springvalidation.dto.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.Min;


@RestController
@Validated
public class TestController {
    
    @PostMapping("save")
    public String saveUser(@RequestBody @Validated User user) {
        return "validation success";
    }

    @GetMapping("{id}")
    public String getUser(@PathVariable("id") @Min(0L) int id) {
        return "validation success";
    }

	
    @GetMapping("get")
    public String getUser2(@RequestParam("id") @Min(0L) @NotNull int id) {
        return "validation success 2";
    }
}


当出现不符合要求的id时

getUser2 接口测试,当分别访问

 localhost:8080/get?id=null
 localhost:8080/get?id=""
 localhost:8080/get?id=-1

结果分别为

2、进阶使用 2.1、分组校验

dto修改如下

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotNull;


@Data
public class User {
    @NotNull(message="null自定义提示")
    @Length(min = 2, max = 10, message="长度自定义提示")
    private String username;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 2, max = 10, groups = {Save.class, Update.class})
    private String pwd;

    private String id;

    public interface Save { }

    public interface Update { }
}

接口修改如下:

@PostMapping("save")
public String saveUser(@RequestBody @Validated(User.Save.class) User user) {
     return "validation success";
 }

自测~

2.2、嵌套校验

当一个对象携带其他对象内的参数也需要校验的时候,可以使用 @Valid 来进行嵌套,可以重新定义Authority 类的校验规则,也可重新建一个类,进行校验规则复用

dto修改如下

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;


@Data
public class User {
    @NotNull(message="null自定义提示")
    @Length(min = 2, max = 10, message="长度自定义提示")
    private String username;

    @NotNull(groups = {Save.class, Update.class})
    @Length(min = 2, max = 10, groups = {Save.class, Update.class})
    private String pwd;

    private String id;

    @NotNull(groups = {Save.class, Update.class})
    @Valid
    private Authority authority;

    @Data
    public static class Authority {
        @Min(value = 1, groups = Update.class)
        private Long id;

        @NotNull(groups = {Save.class, Update.class})
        @Length(min = 2, max = 10, groups = {Save.class, Update.class})
        private String name;
        
        //更多...
    }

    public interface Save { }

    public interface Update { }
}

这样 对于 User 内的所有字段都会校验,包括 Authority 内部定义的规则

细心的会发现了一个问题

@Valid和@Validated区别

区别@Valid@Validated
提供者JSR-303 规范Spring
是否支持分组不支持支持
标注位置METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USETYPE, METHOD, PARAMETER
嵌套校验支持不支持
2.3 、集合测试

当前端直接传递json字符串来时,可自定义集合测试
新增dto,ValidationList

import lombok.experimental.Delegate;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;


public class ValidationList implements List {
    
    @Delegate
    @Valid
    public List list = new ArrayList<>();

    @Override
    public String toString() {
        return list.toString();
    }
}

新增接口

@PostMapping("/saveList")
public String saveList(@RequestBody @Validated(User.Save.class) ValidationList userList) {
    return "validation success";
}



这样整个json里面的参数,就会按照我们定义的规则(User)进行校验,按照规则,各位看官可根据规则,键入不合符规则数据自行测试

2.4 、自定义校验

实现 ConstraintValidator 接口,编写约束校验器,即定义校验规则,这里我们验证密码,由数字或者a-d的字母组成,2-10长度

import com.example.springvalidation.annotation.SjtValidation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class SjtValidator implements ConstraintValidator {

	//自定义校验规则
    private static final Pattern PATTERN = Pattern.compile("^[a-d\d]{2,10}$");

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

        if (value != null) {
            Matcher matcher = PATTERN.matcher(value);
            return matcher.find();
        }
        return true;
    }
}

新增注解

import com.example.springvalidation.validator.SjtValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.RetentionPolicy.RUNTIME;


@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})@Retention(RUNTIME)
@documented
@Constraint(validatedBy = {SjtValidator.class})
public @interface SjtValidation {
    //模仿现有的注解,实现默认的提示消息返回
    String message() default "密码格式错误";

    //支持分组
    Class[] groups() default {};

    Class[] payload() default {};
}

为了干净,新增dto,User2,使用 @SjtValidation

import com.example.springvalidation.annotation.SjtValidation;
import lombok.Data;

import javax.validation.constraints.NotNull;


@Data
public class User2 {
    private String id;
    private String username;

    @NotNull(groups = {User.Save.class, User.Update.class})
    @SjtValidation
    private String pwd;
}

新增接口

@PostMapping("save2")
public String saveUser2(@RequestBody @Validated User2 user2) {
    return "validation success";
}

测试即可,可以发现,我们的密码需要按照我们的规则来进行传递,各位看官可在此基础上自由发挥,自由验证,自由组合前面的各种校验测试

2.5 编程式校验

注入javax.validation.Validator,调用api进行校验

@Resource
private javax.validation.Validator validator;

@PostMapping("/save3")
public String saveUser3(@RequestBody User user) {
   Set> validate = validator.validate(user, User.Save.class);
   if (validate.isEmpty()) {
       return "validation failed";
   } else {
       for (ConstraintViolation constraintViolation : validate) {
           System.out.println(constraintViolation);
       }
       return "validation success";
   }
}

这里不在进行测试,我个人不是特别喜欢这种方式。。

3、fail-fast

Spring Validation默认会校验完所有字段,然后才抛出异常,启动此机制,碰到错误的校验直接返回,后面的字段不在校验

启动类添加bean实现快速失败

import org.hibernate.validator.Hibernatevalidator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

@SpringBootApplication
public class SpringvalidationApplication {
	//校验快速失败
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(Hibernatevalidator.class)
                .configure()
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
    public static void main(String[] args) {
        SpringApplication.run(SpringvalidationApplication.class, args);
    }

}
4、更多注解

更多的内容需要各位看官自行去挖掘啦~

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/671903.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号