IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot中参数校验 Validation -> 正文阅读

[Java知识库]SpringBoot中参数校验 Validation

数据校验很重要,在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。

虽然我们通过 if/else 语句对请求的每一个参数一一校验。但是这样写起来太麻烦了,而且破坏了单一职责原则。所以今天我们来看一下 Spring Boot validation 。

首先我们先来看两个注解

1. @Validated和@Valid 的区别

@Validated:
可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上; 用在方法入参上无法单独提供嵌套验证功能;能配合嵌套验证注解 @Valid 进行嵌套验证。

@Valid:
可以用在方法、构造函数、方法参数和成员属性(字段)上;
用在方法入参上无法单独提供嵌套验证功能;能够用在成员属性(字段)上,提示验证框架进行嵌套验证;;能配合嵌套验证注解 @Valid 进行嵌套验证。

ex1:

package com.scaffold.test.entity;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;


/**
 * 伙伴
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class Mate implements Serializable {

    private static final long serialVersionUID=1L;

    @NotBlank(message = "小伙伴的name不能为空")
    private String name;

    @NotNull(message = "小伙伴的age不能为空")
    private Integer age;
}

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Range;
import org.springframework.data.annotation.Id;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;

@Data
@EqualsAndHashCode(callSuper = false)
public class Student implements Serializable {

    private static final long serialVersionUID=1L;

    @Id
    @Range(min = 1, message = "id不能为空")
    private int id;

    @NotBlank(message = "name不能为空")
    private String name;

    @NotNull(message = "age不能为空")
    private Integer age;

    // 伙伴列表
    @Valid // 嵌套验证必须用@Valid
    @NotNull(message = "mateList不能为空")
    @Size(min = 1, message = "至少需要一个小伙伴")
    private List<Mate> mateList;

}

    /**
     * 添加学生
     * @param student
     * @return
     */
    @PostMapping("post")
    public Result postStudent(@Validated @RequestBody Student student) {
        return ResultGenerator.setSuccessResult(student);
    }

除此之外,
@Validated:支持分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制;
@Valid:不支持分组;

ex2:当更新一个Student时需要校验ID,当新增一个Student时,不需要校验ID,所以这种情况,需要有不同的验证机制。

定义两个接口

import javax.validation.groups.Default;

/**
 * 插入数据分组
 */
public interface Insert extends Default {

}
import javax.validation.groups.Default;

/**
 * 更新数据分组
 */
public interface Update extends Default {

}

然后后在需要校验的字段上加入分组:

@Id
@Range(min = 1, message = "id不能为空", groups = Update.class)
private int id;

最后根据需要,在Controller处理请求中加入@Validated注解并引入需要校验的分组

 /**
     * 添加学生
     * @param student
     * @return
     */
    @PostMapping("post")
    public Result postStudent(@Validated(Insert.class) @RequestBody Student student) {
        return ResultGenerator.setSuccessResult(student);
    }
    /**
     * 更新学生
     * @param student
     */
    @PostMapping("update")
    public Result updateStudent(@Validated(Update.class) @RequestBody Student student) {
        return ResultGenerator.setSuccessResult(student);
    }
    

2. 常用校验注解总结

JSR 提供的校验注解:

在这里插入图片描述

Hibernate Validator 提供的校验注解:
在这里插入图片描述

3.使用validation

如果我们不使用数据校验的话,最普通的方法就是使用if- else
语句进行校验。

@RestController
@RequestMapping("/api/person")
public class PersonController {

    @PostMapping
    public ResponseEntity<PersonRequest> save(@RequestBody PersonRequest personRequest) {
        if (personRequest.getClassId() == null
                || personRequest.getName() == null
                || !Pattern.matches("(^Man$|^Woman$|^UGM$)", personRequest.getSex())) {

        }
        return ResponseEntity.ok().body(personRequest);
    }
}

如果开发普通 Java 程序的的话,你需要可能需要像下面这样依赖:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.9.Final</version>
</dependency>
<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.6</version>
</dependency>

Spring Boot 2.3 1 之后,spring-boot-starter-validation 已经不包括在了 spring-boot-starter-web 中,需要我们手动加上。

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

3.1验证 Controller 的输入

验证请求体
验证请求体即使验证被 @RequestBody 注解标记的方法参数。

我们在需要验证的参数上加上了@Valid注解,如果验证失败,它将抛出MethodArgumentNotValidException。默认情况下,Spring 会将此异常转换为 HTTP Status 400(错误请求)。

controller

@RestController
@RequestMapping("/api/person")
@Validated
public class PersonController {

    @PostMapping
    public ResponseEntity<PersonRequest> save(@RequestBody @Valid PersonRequest personRequest) {
        return ResponseEntity.ok().body(personRequest);
    }
}

entity
我们使用校验注解对请求的参数进行校验!

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PersonRequest {

    @NotNull(message = "classId 不能为空")
    private String classId;

    @Size(max = 33)
    @NotNull(message = "name 不能为空")
    private String name;

    @Pattern(regexp = "(^Man$|^Woman$|^UGM$)", message = "sex 值不在可选范围")
    @NotNull(message = "sex 不能为空")
    private String sex;

}
@RestController
@RequestMapping("/api")
public class ExceptionController {

    @GetMapping("/illegalArgumentException")
    public void throwException() {
        throw new IllegalArgumentException();
    }

    @GetMapping("/resourceNotFoundException")
    public void throwException2() {
        throw new ResourceNotFoundException();
    }
}

在这里插入图片描述
验证请求参数
验证请求参数(Path Variables 和 Request Parameters)即是验证被 @PathVariable 以及 @RequestParam 标记的方法参数。

在类上加上 Validated 注解了,这个参数可以告诉 Spring 去校验方法参数。

@RestController
@RequestMapping("/api/persons")
@Validated
public class PersonController {

    @GetMapping("/{id}")
    public ResponseEntity<Integer> getPersonByID(@Valid @PathVariable("id") @Max(value = 5, message = "超过 id 的范围了") Integer id) {
        return ResponseEntity.ok().body(id);
    }

    @PutMapping
    public ResponseEntity<String> getPersonByName(@Valid @RequestParam("name") @Size(max = 6, message = "超过 name 的范围了") String name) {
        return ResponseEntity.ok().body(name);
    }
}
@ExceptionHandler(ConstraintViolationException.class)
  ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
     return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
  }

在这里插入图片描述
在这里插入图片描述
验证 Service 中的方法
我们还可以验证任何 Spring Bean 的输入,而不仅仅是 Controller 级别的输入。通过使用@Validated和@Valid注释的组合即可实现这一需求!

在类上加上 Validated 注解了,这个参数可以告诉 Spring 去校验方法参数。

@Service
@Validated
public class PersonService {

    public void validatePersonRequest(@Valid PersonRequest personRequest) {
        // do something
    }

}

3.自定义Validation 注解类(实用)

校验特定字段的值是否在可选范围
比如我们现在多了这样一个需求:PersonRequest 类多了一个 Region 字段,Region 字段只能是China、China-Taiwan、China-HongKong这三个中的一个。

①建一个注解 Region。

@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = RegionValidator.class)
@Documented
public @interface Region {

    String message() default "Region 值不在可选范围内";

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

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

②实现 ConstraintValidator接口,并重写isValid 方法。

public class RegionValidator implements ConstraintValidator<Region, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        HashSet<Object> regions = new HashSet<>();
        regions.add("China");
        regions.add("China-Taiwan");
        regions.add("China-HongKong");
        return regions.contains(value);
    }
}


③使用这个注解

@Region
private String region;
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-31 23:49:43  更:2022-03-31 23:52:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:58:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码