【在SpringBoot项目中使用Validation框架检查数据格式】

目录

1. 添加依赖

2. 检查POJO类型的请求参数

3. 关于响应的消息文本

4.  快速失败

5. 检查未封装的请求参数


1. 添加依赖

pom.xml中添加spring-boot-starter-validation依赖项:

<!-- Spring Boot Validation框架,用于检查数据格式 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 检查POJO类型的请求参数

首先,在POJO参数前添加@Valid@Validated注解,用于表示此参数是需要通过Validation框架进行检查的!

 例如:

@ApiOperation("添加相册")
@PostMapping("/add-new")
//                       ↓ 新添加的注解
public JsonResult addNew(@Valid AlbumAddNewDTO albumAddNewDTO) {
    albumService.addNew(albumAddNewDTO);
    return JsonResult.ok();
}

然后,在此参数的属性上添加检查注解,例如添加@NotNull注解,表示将对此属性进行检查,此属性的值不允许为null

@Data
public class AlbumAddNewDTO implements Serializable {

    @ApiModelProperty(value = "相册名称", required = true)
    @NotNull // 新添加的注解
    private String name;
    
    // 省略后续代码

完成后,重启服务,通过API文档提交请求时,故意不提交name(将对应的输入框删除),则服务器端尝试接收的name就会是null,无法通过@NotNull的检查规则,默认将出现400错误,例如:

{
  "timestamp": "2022-12-01T06:38:26.020+00:00",
  "status": 400,
  "error": "Bad Request",
  "path": "/album/add-new"
}

并且,在服务器端的控制台会提示以下错误:

2022-12-01 14:38:26.017  WARN 10708 --- [io-9080-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'albumAddNewDTO' on field 'name': rejected value [null]; codes [NotNull.albumAddNewDTO.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [albumAddNewDTO.name,name]; arguments []; default message [name]]; default message [不能为null]]

可以看到,当检查不通过时,将出现org.springframework.validation.BindException异常!

ServiceCode中添加新的枚举值:

 * 错误:请求参数格式有误
 */
ERR_BAD_REQUEST(400),

GlobalExceptionHandler中添加处理BindException异常的代码:

@ExceptionHandler
public JsonResult handleBindException(BindException e) {
    String message = "请求参数格式错误!";
    return JsonResult.fail(ServiceCode.ERR_BAD_REQUEST, message);
}

3. 关于响应的消息文本

所有检查注解都可以配置检查不通过后的提示文本,例如:

@NotNull(message = "必须提交相册名称")

在处理异常时,应该将此处配置的文本响应到客户端去,可以通过异常对象获取以上文本!

由于客户端提交的若干个请求参数可能有多种错误,则异常对象中可能封装了多个错误信息!如果需要显示所有错误,应该先获取全部信息,然后将这些错误信息组织起来,并响应到客户端去!

所以,处理异常的代码可以调整为:

@ExceptionHandler
public JsonResult handleBindException(BindException e) {
    String delimiter = ",";
    String prefix = "添加相册失败,";
    String suffix = "!";
    StringJoiner stringJoiner = new StringJoiner(delimiter, prefix, suffix);
    List<FieldError> fieldErrors = e.getFieldErrors();
    for (FieldError fieldError : fieldErrors) {
        String defaultMessage = fieldError.getDefaultMessage();
        stringJoiner.add(defaultMessage);
    }
    return JsonResult.fail(ServiceCode.ERR_BAD_REQUEST, stringJoiner.toString());
}

4.  快速失败

Validation框架有快速失败的机制,默认是未开启的,当客户端提交的请求参数有多种错误时,会进行全部的检查,发现所有错误!如果开启快速失败,当检查出第1个错误时,就会停止检查!

配置快速失败,需要创建Valiator类型的对象,通过此对象进行配置,并且,此对象必须被保存在Spring容器中,框架会自动应用它!

当需要创建某个对象并使它保存在Spring容器中,可以在配置类中添加@Bean方法,此方法返回相关对象,在启动项目时,Spring框架会自动调用此方法并将返回保存在Spring容器中。

在项目的根包下创建config.ValidationConfiguration配置类,在此配置类中通过@Bean方法返回Validator对象,并在返回之前将此对象配置为快速失败的:

import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.Validation;

@Slf4j
@Configuration
public class ValidationConfiguration {

    public ValidationConfiguration() {
        log.debug("创建配置类对象:ValidationConfiguration");
    }

    @Bean
    public javax.validation.Validator validator() {
        return Validation.byProvider(HibernateValidator.class)
                .configure() // 开始配置
                .failFast(true) // 配置快速失败
                .buildValidatorFactory() // 构建Validator工厂
                .getValidator(); // 从Validator工厂中获取Validator对象
    }

}

当配置了快速失败后,无论请求参数有多少种错误,都会在发现第1个错误后停止检查,所以,处理异常时,只需要直接获取错误对象即可,不必获取所有错误对象!即:

@ExceptionHandler
public JsonResult handleBindException(BindException e) {
    StringBuilder stringBuilder = new StringBuilder();
    String prefix = "添加相册失败,";
    String suffix = "!";
    String defaultMessage = e.getFieldError().getDefaultMessage();
    stringBuilder.append(prefix).append(defaultMessage).append(suffix);
    return JsonResult.fail(ServiceCode.ERR_BAD_REQUEST, stringBuilder.toString());
}

5. 检查未封装的请求参数

如果某些请求的参数较少,或各参数并不相关,则不会将它们封装在一起,例如:

// http://localhost:9080/brand/delete
@PostMapping("/delete")
public void delete(Long id) {
}

当需要检查这类参数时,首先,需要在当前类上添加@Validated注解,例如:

@RestController
@RequestMapping("/brand")
@Validated // 新添加的注解
public class BrandController {
    
    // 省略其它代码

然后,在请求参数上添加检查注解,例如:

// http://localhost:9080/brand/delete
@PostMapping("/delete")
//                 ↓ 新添加的注解
public void delete(@Range(min = 1, max = 996) @RequestParam Long id) {
}

**提示:**以上@RequestParam在此处并无实际意义,但是,不添加此注解的话,在线API文档的调试界面中不会出现此参数的输入框,所以暂时添加上此注解,后续将删除!

完成后,重启项目,在API文档中调整,如果提交的id参数值不在[1, 996]范围内,将出现500错误(注意:不要处理RuntimeException),则控制台会提示错误:

javax.validation.ConstraintViolationException: delete.id: 需要在1和996之间

则应该在全局异常处理器添加对以上异常的处理:

@ExceptionHandler
public JsonResult handleConstraintViolationException(ConstraintViolationException e) {
    Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
    String delimiter = ",";
    StringJoiner stringJoiner = new StringJoiner(delimiter);
    for (ConstraintViolation<?> constraintViolation : constraintViolations) {
        stringJoiner.add(constraintViolation.getMessage());
    }
    return JsonResult.fail(ServiceCode.ERR_BAD_REQUEST, stringJoiner.toString());
}

个人主页:居然天上楼

感谢你这么可爱帅气还这么热爱学习~~

人生海海,山山而川

你的点赞👍 收藏⭐ 留言📝 加关注✅

是对我最大的支持与鞭策

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

居然天上楼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值