前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >强悍的Spring之spring validation

强悍的Spring之spring validation

作者头像
你呀不牛
发布2021-05-28 11:09:15
3940
发布2021-05-28 11:09:15
举报
文章被收录于专栏:我要变牛我要变牛

spring validation实现对Restful请求的数据进行校验


1、前言

数据的校验是交互式网站一个不可或缺的功能,前端的js校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验。但是为了避免用户绕过浏览器,使用http工具直接向后端请求一些违法数据,服务端的数据校验也是必要的,可以防止脏数据落到数据库中,如果数据库中出现一个非法的邮箱格式,也会让运维人员头疼不已。可以使用本文将要介绍的validation来对数据进行校验。

2、常用校验

  1. JSR303/JSR-349: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其的升级版本,添加了一些新特性。
    • @Null 被注释的元素必须为null
    • @NotNull 被注释的元素必须不为null
    • @AssertTrue 被注释的元素必须为true
    • @AssertFalse 被注释的元素必须为false
    • @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    • @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    • @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    • @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    • @Size(max, min) 被注释的元素的大小必须在指定的范围内
    • @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
    • @Past 被注释的元素必须是一个过去的日期
    • @Future 被注释的元素必须是一个将来的日期
    • @Pattern(value) 被注释的元素必须符合指定的正则表达式
  2. hibernate validation:hibernate validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等
    • @Email 被注释的元素必须是电子邮箱地址
    • @Length 被注释的字符串的大小必须在指定的范围内
    • @NotEmpty 被注释的字符串的必须非空
    • @Range 被注释的元素必须在合适的范围内
  3. spring validation:spring validation对hibernate validation进行了二次封装,在springmvc模块中添加了自动校验,并将校验信息封装进了特定的类中

3、spring boot的数据自动校验功能

3.1 引入依赖

spring-web模块使用了hibernate-validation,并且databind模块也提供了相应的数据绑定功能。

代码语言:javascript
复制
<dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

我们只需要引入spring-boot-starter-web依赖即可,如果查看其子依赖,可以发现如下的依赖:

代码语言:javascript
复制
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
3.2 构建启动类
代码语言:javascript
复制
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
        System.out.println("Start app success.");
    }
}
3.3 创建需要被校验的实体类
代码语言:javascript
复制
public class Person {
@NotEmpty(message = "name不能为空")
private String name;
@Range(min = 0, max = 100, message = "age不能大于100小于0")
private int age;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}
3.4 在Controller中校验数据

springmvc为我们提供了自动封装表单参数的功能,一个添加了参数校验的典型controller如下所示。

代码语言:javascript
复制
@RequestMapping("/test")
public String valid(@Validated Person person, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            System.out.println(fieldError);
        }
        return "fail";
    }
    return "success";
}

值得注意的地方:

  1. 参数Persison前需要加上@Validated注解,表明需要spring对其进行校验,而校验的信息会存放到其后的BindingResult中。注意,必须相邻,如果有多个参数需要校验,形式可以如下。valid(@Validated Person person, BindingResult fooBindingResult ,@Validated Bar bar, BindingResult barBindingResult);即一个校验类对应一个校验结果。
  2. 校验结果会被自动填充,在controller中可以根据业务逻辑来决定具体的操作,如跳转到错误页面。 一个最基本的校验就完成了.

启动容器测试结果如下:

测试: http://localhost:8080/valid?age=105&name=steven

代码语言:javascript
复制
Field error in object 'person' on field 'age': rejected value [105]; codes [Range.person.age,Range.age,Range.int,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.age,age]; arguments []; default message [age],100,0]; default message [age不能大于100小于0]
3.5 统一异常处理

前面那种方式处理校验错误,略显复杂,而且一般网站都会对请求错误做统一的404页面封装,如果数据校验不通过,则Spring boot会抛出BindException异常,我们可以捕获这个异常并使用Result封装返回结果。通过@RestControllerAdvice定义异常捕获类。

Controller类:

代码语言:javascript
复制
@RequestMapping(value = "valid", method = RequestMethod.GET)
public String valid(@Validated Person person) {
    System.out.println(person);
    return "success";
}

统一异常处理类:

代码语言:javascript
复制
@RestControllerAdvice
public class BindExceptionHanlder {
    @ExceptionHandler(BindException.class)
    public String handleBindException(HttpServletRequest request, BindException exception) {
        List<FieldError> allErrors = exception.getFieldErrors();
        StringBuilder sb = new StringBuilder();
        for (FieldError errorMessage : allErrors) {
            sb.append(errorMessage.getField()).append(": ").append(errorMessage.getDefaultMessage()).append(", ");
        }
        System.out.println(sb.toString());
        return sb.toString();
    }
}

测试: http://localhost:8080/valid?age=105&name=steven

输出:age: age不能大于100小于0,

4、自定义校验注解

4.1 @NameValidation
代码语言:javascript
复制
@Documented
@Constraint(validatedBy = NameValidationValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RUNTIME)
public @interface NameValidation {
    String message() default "不是合法的名字";

    Class<?>[] groups() default {};

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

    @Target({PARAMETER, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        NameValidation[] value();
    }
}
4.2 校验类NameValidationValidator
代码语言:javascript
复制
public class NameValidationValidator implements ConstraintValidator<NameValidation, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if ("steven".equalsIgnoreCase(value)) {
            return true;
        }
        String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
        System.out.println("default message :" + defaultConstraintMessageTemplate);
        //禁用默认提示信息
        //context.disableDefaultConstraintViolation();
        //设置提示语
        //context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
        return false;
    }
}
4.3 在Person类增加新注解
代码语言:javascript
复制
@NotEmpty(message = "name不能为空")
@NameValidation
private String name;

测试: http://localhost:8080/valid?age=105&name=lxy

输出:age: age不能大于100小于0, name: 不是合法的名字,

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 你呀不牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • spring validation实现对Restful请求的数据进行校验
    • 1、前言
      • 2、常用校验
        • 3、spring boot的数据自动校验功能
          • 3.1 引入依赖
          • 3.2 构建启动类
          • 3.3 创建需要被校验的实体类
          • 3.4 在Controller中校验数据
          • 3.5 统一异常处理
        • 4、自定义校验注解
          • 4.1 @NameValidation
          • 4.2 校验类NameValidationValidator
          • 4.3 在Person类增加新注解
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档