文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Boot 中怎么利用JSR303 实现参数验证

2023-06-05 04:06

关注

今天就跟大家聊聊有关Spring Boot 中怎么利用JSR303 实现参数验证,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

Bean Validation 规范内嵌的约束注解

Spring Boot 中怎么利用JSR303 实现参数验证

实例

基本应用

引入依赖

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

给参数对象添加校验注解

@Datapublic class User {    private Integer id;    @NotBlank(message = "用户名不能为空")    private String username;    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")    private String password;    @Email    private String email;    private Integer gender;}

Controller 中需要校验的参数Bean前添加 @Valid 开启校验功能,紧跟在校验的Bean后添加一个BindingResult,BindingResult封装了前面Bean的校验结果。

@RestController@RequestMapping("/user")public class UserController {    @PostMapping("")    public Result save (@Valid User user , BindingResult bindingResult)  {        if (bindingResult.hasErrors()) {            Map<String , String> map = new HashMap<>();            bindingResult.getFieldErrors().forEach( (item) -> {                String message = item.getDefaultMessage();                String field = item.getField();                map.put( field , message );            } );            return Result.build( 400 , "非法参数 !" , map);        }        return Result.ok();    }}

测试如下:

Spring Boot 中怎么利用JSR303 实现参数验证

异常的统一处理

参数校验不通过时,会抛出 BingBindException 异常,可以在统一异常处理中,做统一处理,这样就不用在每个需要参数校验的地方都用 BindingResult 获取校验结果了。

@Slf4j@RestControllerAdvice(basePackages = "com.itwolfed.controller")public class GlobalExceptionControllerAdvice {    @ExceptionHandler(value= {MethodArgumentNotValidException.class , BindException.class})    public Result handleVaildException(Exception e){        BindingResult bindingResult = null;        if (e instanceof MethodArgumentNotValidException) {            bindingResult = ((MethodArgumentNotValidException)e).getBindingResult();        } else if (e instanceof BindException) {            bindingResult = ((BindException)e).getBindingResult();        }        Map<String,String> errorMap = new HashMap<>(16);        bindingResult.getFieldErrors().forEach((fieldError)->                errorMap.put(fieldError.getField(),fieldError.getDefaultMessage())        );        return Result.build(400 , "非法参数 !" , errorMap);    }}

分组解决校验

新增和修改对于实体的校验规则是不同的,例如id是自增的时,新增时id要为空,修改则必须不为空;新增和修改,若用的恰好又是同一种实体,那就需要用到分组校验。

校验注解都有一个groups属性,可以将校验注解分组,我们看下@NotNull的源码:

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })@Retention(RUNTIME)@Repeatable(List.class)@Documented@Constraint(validatedBy = { })public @interface NotNull {    String message() default "{javax.validation.constraints.NotNull.message}";    Class<?>[] groups() default { };    Class<? extends Payload>[] payload() default { };    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })    @Retention(RUNTIME)    @Documented    @interface List {        NotNull[] value();    }}

从源码可以看出 groups 是一个Class<?>类型的数组,那么就可以创建一个Groups.

public class Groups {    public interface Add{}    public interface  Update{}}

给参数对象的校验注解添加分组

@Datapublic class User {    @Null(message = "新增不需要指定id" , groups = Groups.Add.class)    @NotNull(message = "修改需要指定id" , groups = Groups.Update.class)    private Integer id;    @NotBlank(message = "用户名不能为空")    @NotNull    private String username;    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")    private String password;    @Email    private String email;    private Integer gender;}

Controller 中原先的@Valid不能指定分组 ,需要替换成@Validated

@RestController@RequestMapping("/user")public class UserController {    @PostMapping("")    public Result save (@Validated(Groups.Add.class) User user)  {        return Result.ok();    }}

测试如下:

Spring Boot 中怎么利用JSR303 实现参数验证

自定义校验注解

虽然JSR303和springboot-validator 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解。

例如User中的gender,用 1代表男 2代表女,我们自定义一个校验注解@ListValue,指定取值只能1和2。

创建约束规则
@Documented@Constraint(validatedBy = { ListValueConstraintValidator.class })@Target({ METHOD, FIELD, ANNOTATION_TYPE })@Retention(RUNTIME)public @interface ListValue {    String message() default "";    Class<?>[] groups() default { };    Class<? extends Payload>[] payload() default { };    int[] vals() default { };}

一个标注(annotation) 是通过@interface关键字来定义的. 这个标注中的属性是声明成类似方法
的样式的. 根据Bean Validation API 规范的要求:

除了这三个强制性要求的属性(message, groups 和 payload) 之外, 我们还添
加了一个属性用来指定所要求的值. 此属性的名称vals在annotation的定义中比较特
殊, 如果只有这个属性被赋值了的话, 那么, 在使用此annotation到时候可以忽略此属性名称.

另外, 我们还给这个annotation标注了一些元标注( meta
annotatioins):

创建约束校验器
import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import java.util.HashSet;import java.util.Set;public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {    private Set<Integer> set = new HashSet<>();        @Override    public void initialize(ListValue constraintAnnotation) {        int[] vals = constraintAnnotation.vals();        for (int val : vals) {            set.add(val);        }    }        @Override    public boolean isValid(Integer value, ConstraintValidatorContext context) {        return set.contains(value);    }}

ListValueConstraintValidator定义了两个泛型参数, 第一个是这个校验器所服务到标注类型(在我们的例子中即ListValue), 第二个这个校验器所支持到被校验元素的类型 (即Integer)。

如果一个约束标注支持多种类型的被校验元素的话, 那么需要为每个所支持的类型定义一个ConstraintValidator,并且注册到约束标注中。

这个验证器的实现就很平常了, initialize() 方法传进来一个所要验证的标注类型的实例, 在本
例中, 我们通过此实例来获取其vals属性的值,并将其保存为Set集合中供下一步使
用。

isValid()是实现真正的校验逻辑的地方, 判断一个给定的int对于@ListValue这个约束条件来说
是否是合法的。

在参数对象中使用@ListValue注解。

@Datapublic class User {    @Null(message = "新增不需要指定id" , groups = Groups.Add.class)    @NotNull(message = "修改需要指定id" , groups = Groups.Update.class)    private Integer id;    @NotBlank(message = "用户名不能为空")    @NotNull    private String username;    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")    private String password;    @Email    private String email;    @ListValue( message = "性别应指定相应的值" , vals = {1,2} , groups = {Groups.Add.class , Groups.Update.class})    private Integer gender;}

测试如下:

Spring Boot 中怎么利用JSR303 实现参数验证

看完上述内容,你们对Spring Boot 中怎么利用JSR303 实现参数验证有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程网行业资讯频道,感谢大家的支持。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯