Pre
SpringBoot - 优雅的实现【自定义参数校验】高级进阶
SpringBoot - 优雅的实现【参数分组校验】高级进阶
SpringBoot - 使用Assert校验让业务代码更简洁
概述
日常开发中,对入参进行参数校验是必不可少的一个环节。 而使用最多的就是Validator框架 。
Validator校验框架遵循了JSR-303 【Java Specification Requests】验证规范 。
这里我们探讨下,在boot项目中如何优雅的集成参数校验框架
参数校验三部曲
Step1 搞依赖
boot 2.3 以后版本的pom信息如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
- spring boot-2.3之前的版本只需要引入 spring-boot-starter-web 即可 ,已经包含了
- spring boot-2.3及以后的版本,校验包是一个单独的starter,需要同时引入spring-boot-starter-validation
Step2 搞参数校验的实体类
package com.artisan.vo;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
@Data
public class Artisan {
private String id;
@NotBlank(message = "名字为必填项")
private String name;
@Length(min = 8, max = 12, message = "password长度必须位于8到12之间")
private String password;
@Email(message = "请填写正确的邮箱地址")
private String email;
private String sex;
@NotEmpty(message = "Code不能为空")
private String code;
}
常用的校验注解
| 注解 | 功能 |
|---|---|
| @AssertFalse | 可以为null,如果不为null的话必须为false |
| @AssertTrue | 可以为null,如果不为null的话必须为true |
| @DecimalMax | 设置不能超过最大值 |
| @DecimalMin | 设置不能超过最小值 |
| @Digits | 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内 |
| @Future | 日期必须在当前日期的未来 |
| @Past | 日期必须在当前日期的过去 |
| @Max | 最大不得超过此最大值 |
| @Min | 最大不得小于此最小值 |
| @NotNull | 不能为null,可以是空 |
| @Null | 必须为null |
| @Pattern | 必须满足指定的正则表达式 |
| @Size | 集合、数组、map等的size()值必须在指定范围内 |
| 必须是email格式 | |
| @Length | 长度必须在指定范围内 |
| @NotBlank | 字符串不能为null,字符串trim()后也不能等于“” |
| @NotEmpty | 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“” |
| @Range | 值必须在指定范围内 |
| @URL | 必须是一个URL |
Step3 开始验证
注意看注释
package com.artisan.controller;
import com.artisan.vo.Artisan;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.Email;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
@RestController
@Slf4j
@Validated
@RequestMapping("/valid")
public class ArtisanController {
/**
* 使用@RequestBody注解,用于接受前端发送的json数据
*
* @param artisan
* @return
*/
@PostMapping("/testJson")
public String testJson(@Validated @RequestBody Artisan artisan) {
log.info("InComing Param {}", artisan);
return "testJson valid success";
}
/**
* 模拟表单提交
*
* @param artisan
* @return
*/
@PostMapping(value = "/testForm")
public String testForm(@Validated Artisan artisan) {
log.info("InComing Param is {}", artisan);
return "testForm valid success";
}
/**
* 模拟单参数提交
*
* @param email
* @return
*/
@PostMapping(value = "/testParma")
public String testParma(@Email String email) {
log.info("InComing Param is {}", email);
return "testParma valid success";
}
}
-
testJson使用 @RequestBody注解,用于接受前端发送的json数据
-
testForm模拟表单提交
-
testParma模拟单参数提交
当使用单参数校验时需要在Controller上加上@Validated注解,否则不生效
【测试第一个接收JSON的接口 】
可以看到抛出的异常为: org.springframework.web.bind.MethodArgumentNotValidException
【测试第二个接收表单的接口 】
可以看到抛出的异常为: org.springframework.validation.BindException
【测试第三个接收单参数的接口 】
可以看到抛出的异常为:javax.validation.ConstraintViolationException
存在的问题
且不说好不好看, 不管怎么样,现在是通过Validation框架实现了校验。 当然了,我们的追求肯定不是这样的,Validator校验框架返回的错误提示太臃肿了 ,格式啥的都不一样,很难搞哦, 怎么给前台返回????
使用 统一格式 + 全局异常Handler 优化
增加统一返回 和 全局异常Handler,单独拦截参数校验的三个异常:javax.validation.ConstraintViolationException,org.springframework.validation.BindException,org.springframework.web.bind.MethodArgumentNotValidException
/**
* @param e
* @return
*/
@ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
public ResponseEntity<ResponseData<String>> handleValidatedException(Exception e) {
ResponseData<String> resp = null;
if (e instanceof MethodArgumentNotValidException) {
// BeanValidation exception
MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
resp = ResponseData.fail(HttpStatus.BAD_REQUEST.value(),
ex.getBindingResult().getAllErrors().stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining("; "))
);
} else if (e instanceof ConstraintViolationException) {
// BeanValidation GET simple param
ConstraintViolationException ex = (ConstraintViolationException) e;
resp = ResponseData.fail(HttpStatus.BAD_REQUEST.value(),
ex.getConstraintViolations().stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining("; "))
);
} else if (e instanceof BindException) {
// BeanValidation GET object param
BindException ex = (BindException) e;
resp = ResponseData.fail(HttpStatus.BAD_REQUEST.value(),
ex.getAllErrors().stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining("; "))
);
}
log.error("参数校验异常:{}", resp.getMessage());
return new ResponseEntity<>(resp, HttpStatus.BAD_REQUEST);
}
重新测试























还没有评论,来说两句吧...