JSR303校验前端传递的数据方式

 更新时间:2024年01月12日 10:33:59   作者:龙域、白泽  
这篇文章主要介绍了JSR303校验前端传递的数据方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

介绍

JSR-303规范(Bean Validation规范)提供了对 Java EE 和 Java SE 中的 Java Bean 进行验证的方式。

该规范主要使用注解的方式来实现对 Java Bean 的验证功能。

作用

前端传递数据到后端时,可以使用其对Bean对象的属性进行合法性校验。

快速开始

导入依赖

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

Java Bean

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
    @TableId
    private Long id;
 
    @NotBlank(message = "用户名不能为空")
    @TableField("user_name")
    private String userName;
 
    @Size(max = 20,min = 6,message = "密码的长度必须在6-20位")
    @TableField("pass_word")
    private String passWord;
 
    @TableField("group_id")
    private Long groupId;
 
    @Max(value = 100, message = "城市编号不等大于100")
    @TableField("city_id")
    private Long cityId;
 
    @Email(message = "邮件不合法")
    @TableField("email")
    private String email;
}

统一返回结果

统一响应结果枚举类

@Getter
@AllArgsConstructor
@ToString
public enum ResponseEnum {
 
    SUCCESS(0, "成功"),
    ERROR(-1, "服务器内部错误"),
 
    private final Integer code;
 
    private final String message;
}

统一结果返回类

@Data
public class R {
 
    private Integer code;
 
    private String message;
 
    /**
     * 返回的数据
     */
    private Map<String, Object> data = new HashMap<>();
 
    private R() {
    }
 
    public static R ok() {
        return setResult(ResponseEnum.SUCCESS);
    }
 
    public static R error() {
        return setResult(ResponseEnum.ERROR);
    }
 
    /**
     * 设置特定结果
     */
    public static R setResult(ResponseEnum responseEnum) {
        R r = new R();
        r.setCode(responseEnum.getCode());
        r.setMessage(responseEnum.getMessage());
        return r;
    }
 
    /**
     * 设置响应消息
     */
    public R message(String message) {
        this.setMessage(message);
        return this;
    }
 
    public R code(Integer code) {
        this.setCode(code);
        return this;
    }
 
    public R data(String key, Object value) {
        this.data.put(key, value);
        return this;
    }
 
    public R data(Map<String, Object> map) {
        this.setData(map);
        return this;
    }
}

方法一

Bean对象的下一个位置的参数写BindingResult对象,当JSR303校验失败后可以由BindResult对象捕获异常

controller层

@Valid注解后面的对象是要校验的Bean对象

Bean对象的下一个位置的参数写BindingResult对象

@RestController
@RequestMapping("/user")
public class UserController {
 
    @Resource
    private UserServiceImpl userService;
 
    @PostMapping
    public R addUser(@Valid @RequestBody User user, BindingResult bindingResult) {
        Map<String, Object> map = new HashMap<>();
        // 判断是否有错误
        if (bindingResult.hasErrors()) {
            // 如果有错误,遍历错误信息,添加到Map中
            bindingResult.getFieldErrors().forEach((item) -> {
                // 获取错误提示
                String message = item.getDefaultMessage();
                // 获取错误的属性名称
                String field = item.getField();
                map.put(field, message);
            });
            return R.error().message("参数信息错误").data(map);
        }
        userService.save(user);
        return R.ok();
    }
}

方法二

当接口非常多,每一个接口都要写校验非常麻烦,写一个统一异常处理的类来集中处理异常

统一异常处理类

@Slf4j
@Component //Spring容易自动管理
@RestControllerAdvice //在controller层添加通知。当Controller层出现异常,这里的方法会捕获异常,返回错误信息(相当于服务降级)
public class UnifiedExceptionHandler {
 
    /**
     * 参数校验异常处理
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e) {
        log.error("数据校验出现问题{}, 异常类型{}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        Map<String, Object> map = new HashMap<>();
        bindingResult.getFieldErrors().forEach((item) -> {
            map.put(item.getField(), item.getDefaultMessage());
        });
        return R.error().message("参数信息错误").data(map);
    }
}

controller层

接口方法的参数如果有BindResult对象,代表校验出错由BindResult接受异常

接口方法的参数没有BindResult对象,代表校验出错将抛出异常,被统一异常处理类的方法接受

接口方法的参数没有BindResult对象,也没有统一异常处理类的方法接受,就抛出400的异常

@PostMapping
public R addUser(@Valid @RequestBody User user) {
    userService.save(user);
    return R.ok();
}

测试

校验成功的测试

请求体的JSON数据

{
	"userName": "lixianchichi",
	"passWord": "104ee44",
	"groupId": 1,
	"cityId": 14,
	"email": "123456@qq.com"
}

返回的响应信息

{
    "code": 0,
    "message": "成功",
    "data": {}
}

校验失败的测试

请求体的JSON数据

{
	"userName": "lixianchichi",
	"passWord": "10444eeeeeeeeeeeeeeeeee44",
	"groupId": 1,
	"cityId": 1514,
	"email": "123456@qq.com"
}

返回的响应信息

{
    "code": -1,
    "message": "参数信息错误",
    "data": {
        "passWord": "密码的长度必须在6-20位",
        "cityId": "城市编号不等大于100"
    }
}

方法一与方法二测试结果相同,都为如上结果

分组校验

使用场景:不同情况下的校验规则是不同的,如新增的时候自动生成Id,所以数据不需要携带Id,而修改的时候必须要携带Id(不同场景触发不同的校验条件)

创建valid包

包里面创建两个空接口InsertGroup和UpdateGroup,代表新增和修改两种环境。

Java Bean

每一个Bean校验注解都有一个groups属性,值是一个接口字节码对象的数据,用来指定环境。

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
    @TableId
    // 更新的时候校验
    @NotNull(message = "修改用户id不能为null" ,groups = {UpdateGroup.class})
    // 新增的时候校验
    @Null(message = "新增用户id必须为null" ,groups = {InsertGroup.class})
    private Long id;
 
    // 新增和修改的时候都校验
    @NotBlank(message = "用户名不能为空" ,groups = {InsertGroup.class, UpdateGroup.class})
    @TableField("user_name")
    private String userName;
 
    ...
    // 修改的时候判断email是否合法,可以null,不报错(不传就不校验)
    @Email(message = "邮件不合法" ,groups = {UpdateGroup.class})
    @TableField("email")
    private String email;
}

controller层

使用@Validated代替@Valid注解,该注解可以指定环境(接口字节码对象),

如下:代表当前是新增的情况

@PostMapping
public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) {
    userService.save(user);
    return R.ok();
}

注意:没有指定groups属性的注解,在controller层指定环境的情况下,不会生效

测试

在新增环境,前端传递JSON对象如果带Id属性

@PostMapping
public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) {
    userService.save(user);
    return R.ok();
}

响应结果

{
    "code": -1,
    "message": "参数信息错误",
    "data": {
        "id": "新增用户id必须为null"
    }
}

在修改环境,前端传递JSON对象如果带Id属性

@PostMapping
public R addUser(@Validated({UpdateGroup.class}) @RequestBody User user) {
    userService.save(user);
    return R.ok();
}

响应结果

{
    "code": -1,
    "message": "参数信息错误",
    "data": {
        "id": "修改用户id不能为null"
    }
}

自定义校验注解

实现功能:校验Bean的某个属性的字段只能是0和1

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
    ...
    @TableField("group_id")
    @ListValue(vals = {0L, 1L}, groups = {UpdateGroup.class})
    private Long groupId;
    ...
}

自定义的校验注解

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 指定校验器,可以指定多个校验器,会自动适配,如:此注解还有一个Double数组的属性,再添加一个Double类型的校验器,当我们使用注解的时候,我们给Double数组赋值,就会自动找Double类型的校验器校验
@Constraint(validatedBy = {ListValueCondtraintValidator.class})
public @interface ListValue {
    
    // 前三个属性都是每个JSR303注解必须有的
    // 默认错误信息,从properties里获取值
    String message() default "{com.lixianhe.valid.listValue.message}";
 
    Class<?>[] groups() default {};
 
    Class<? extends Payload>[] payload() default {};
    
    long[] vals() default {};
}

自定义校验器

/**
 * 自定义校验器
 */
public class ListValueCondtraintValidator implements ConstraintValidator<ListValue, Long> {
 
    private Set<Long> set = new HashSet<>();
 
    /**
     * 初始化方法
     *
     * @param constraintAnnotation
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        long[] vals = constraintAnnotation.vals();
        for (long val : vals) {
            set.add(val);
        }
    }
 
    /**
     * 判断是否校验成功
     *
     * @param value                      需要校验的值
     * @param constraintValidatorContext
     * @return 校验结果
     */
    @Override
    public boolean isValid(Long value, ConstraintValidatorContext constraintValidatorContext) {
        return set.contains(value);
    }
}

测试

当groupId传2的时候

{
	"userName": "lixianchichi",
	"passWord": "104eefd454545546456565tygpl[per44",
	"groupId": 2,
	"cityId": 10
}

响应结果

{
    "code": -1,
    "message": "参数信息错误",
    "data": {
        "groupId": "必须提交指定的值"
    }
}

补充

Java Bean校验注解总结

限制说明
@Null限制只能为null
@NotNull限制必须不为null
@AssertFalse限制必须为false
@AssertTrue限制必须为true
@DecimalMax(value)限制必须为一个不大于指定值的数字
@DecimalMin(value)限制必须为一个不小于指定值的数字
@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future限制必须是一个将来的日期
@Max(value)限制必须为一个不大于指定值的数字
@Min(value)限制必须为一个不小于指定值的数字
@Past限制必须是一个过去的日期
@Pattern(value)限制必须符合指定的正则表达式
@Size(max,min)限制字符长度必须在min到max之间
@Past验证注解的元素值(日期类型)比当前时间早
@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
@URL校验是否位合法的URL

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java数据结构基础:循环链表和栈

    java数据结构基础:循环链表和栈

    这篇文章主要介绍了Java数据结构之循环链表、栈的实现方法,结合实例形式分析了Java数据结构中循环链表、栈、的功能、定义及使用方法,需要的朋友可以参考下
    2021-08-08
  • Java内部类原理与用法实例总结

    Java内部类原理与用法实例总结

    这篇文章主要介绍了Java内部类原理与用法,结合实例形式总结分析了非静态内部类、静态内部类、局部类等相关概念、原理、用法及相关操作注意事项,需要的朋友可以参考下
    2018-08-08
  • java内存分布实现代码

    java内存分布实现代码

    这篇文章主要介绍了浅谈Java内存区域划分和内存分配策略,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • JDBC的基本操作与Statement和PreparedStateMent使用区别分析

    JDBC的基本操作与Statement和PreparedStateMent使用区别分析

    这篇文章主要介绍了JDBC的基本操作与Statement和PreparedStateMent使用区别,Java Database Connectivity,它是代表一组独立于任何数据库管理系统(DBMS)的API,声明在java.sql与javax.sql包中,是SUN(现在Oracle)提供的一组接口规范
    2023-04-04
  • IDEA部署jeesite3完美运行教程详解

    IDEA部署jeesite3完美运行教程详解

    这篇文章主要介绍了IDEA部署jeesite3完美运行教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Centos中yum方式安装java的实现示例

    Centos中yum方式安装java的实现示例

    这篇文章主要介绍了Centos中yum方式安装java的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • Java Apollo是如何实现配置更新的

    Java Apollo是如何实现配置更新的

    这篇文章主要介绍了Java Apollo是如何实现配置更新的,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • Spring bean 加载执行顺序实例解析

    Spring bean 加载执行顺序实例解析

    这篇文章主要介绍了Spring bean 加载执行顺序实例解析,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • 高并发下如何避免重复数据产生技巧

    高并发下如何避免重复数据产生技巧

    这篇文章主要为大家介绍了高并发下如何避免重复数据的产生技巧详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 详解Spring Cloud Hystrix断路器实现容错和降级

    详解Spring Cloud Hystrix断路器实现容错和降级

    本篇文章主要介绍了详解Spring Cloud Hystrix断路器实现容错和降级,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05

最新评论