spring boot参数验证
必须要知道
- 简述 JSR303/JSR-349,hibernate validation,spring validation 之间的关系
JSR303 是一项标准,JSR-349 是其的升级版本,添加了一些新特性,他们规定一些校验规范即校验注解,如 @Null,@NotNull,@Pattern,他们位于 javax.validation.constraints 包下,只提供规范不提供实现;
hibernate validation 是对这个规范的实践,他提供了相应的实现,并增加了一些其他校验注解,如 @Email,@Length,@Range 等等,他们位于 org.hibernate.validator.constraints 包下;
spring validation,是对 hibernate validation 进行了二次封装,在 springmvc 模块中添加了自动校验,并将校验信息封装进了特定的类中;
spring mvc对spring validation的应用
get请求 参数验证
@RestController
@RequestMapping("/beavalidate")
public class BeanValidateController {
@GetMapping("/testget")
public ResultBody testGet(@Valid @NotBlank @RequestParam("name") String name){
return ResultBody.successBody("get方式验证成功!");
}
}
post请求 参数验证
@RestController
@RequestMapping("/beavalidate")
public class BeanValidateController {
@PostMapping("/testpost")
public ResultBody testPost(@Valid @RequestBody TagAddForm tagAddForm){
return ResultBody.successBody("验证成功!");
}
}
参数验证异常统一处理
如果我们没有对验证错误进行处理,调用接口的客户端无法知道到底是什么参数发生了错误,
还有就是如果前后端分离我们一般都是规定json格式传输数据,所以我们最好针对这个错误进行处理并返回格式化的内容。
定义错误相关的枚举
public interface IResult extends Serializable {
Integer getCode();
String getMessage();
}
public enum ResultEnum implements IResult {
/**
* 成功
*/
SUCCESS(10000, "成功"),
/**
* 请求参数异常
*/
ARGUMENT_EXCEPTION(10001, "请求信息异常");
private Integer code;
private String message;
ResultEnum(Integer code, String msg) {
this.code = code;
this.message = msg;
}
@Override
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
定义统一的json对象
@Data
public class ResultBody<T> {
/**
* 错误码
*/
private Integer code;
/**
* 提示信息
*/
private String message;
/**
* 具体数据
*/
private T body;
private ResultBody() {
}
/**
* 构造成功包体
*
* @return 成功的包体
*/
public static ResultBody successBody() {
return dowith(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), new HashMap<>(1));
}
/**
* 构造成功的包体
*
* @param body 包体中body的内容
* @return 成功的包体
*/
public static ResultBody successBody(Object body) {
return dowith(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), body);
}
/**
* 构造调用失败的包体
*
* @param result 错误码和提示信息内容
* @return 失败的包体
*/
public static ResultBody errorBody(IResult result) {
return dowith(result.getCode(), result.getMessage(), new HashMap<>(1));
}
/**
* 构造调用失败的包体
*
* @param result 错误码和提示信息内容
* @param body 包体内容
* @return 失败的包体
*/
public static ResultBody errorBody(IResult result, Object body) {
return dowith(result.getCode(), result.getMessage(), body);
}
/**
* 构造调用失败的包体
*
* @param result 错误码和提示信息内容
* @return 失败的包体
*/
public static ResultBody errorBody(IResult result, String message) {
return dowith(result.getCode(), message, null);
}
/**
* 构造调用失败的包体
*
* @param result 错误码和提示信息内容
* @param body 包体内容
* @return 失败的包体
*/
public static ResultBody errorBody(IResult result, Object body, String message) {
return dowith(result.getCode(), message, body);
}
}
定义异常处理器
@ControllerAdvice
public class ValidationExceptionHandler {
/**
* 处理post方式参数验证异常
* @param e
* @return
*/
@ExceptionHandler({MethodArgumentNotValidException.class})
@ResponseBody
public ResultBody notValidException(MethodArgumentNotValidException e) {
log.error("API参数校验异常:{}", e);
return this.wrapperBindingResult(e.getBindingResult());
}
/**
* 处理get方式参数验证异常
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler({MissingServletRequestParameterException.class})
public ResultBody requestMissingParamsErrorHandler(MissingServletRequestParameterException e) {
log.error("MissingServletRequestParameterException:{}", e);
String errorMessage = e.getMessage();
ResultEnum resultEnum = ResultEnum.ARGUMENT_EXCEPTION;
return ResultBody.errorBody(resultEnum);
}
private ResultBody wrapperBindingResult(BindingResult bindingResult) {
ResultEnum resultEnum = ResultEnum.ARGUMENT_EXCEPTION;
StringBuilder errorMessage = new StringBuilder();
if ("prod".equals(this.profile)) {
errorMessage.append("请求信息异常");
} else {
Iterator var4 = bindingResult.getFieldErrors().iterator();
while(var4.hasNext()) {
FieldError fieldError = (FieldError)var4.next();
errorMessage.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(";");
}
}
resultEnum.setMessage(errorMessage.toString());
return ResultBody.errorBody(resultEnum);
}
}
效果
-
get方式参数验证
-
post方式参数验证:
非mvc环境下使用验证
在一些情况下我们并不是只是验证http请求参数的绑定,我们还需要java方法之间调用的参数验证。
不适用验证框架我们一般都是手动验证,但是手动验证是很费力的,下面就介绍一下在非mvc的情况下使用spring validation框架进行验证。
手动验证
定义bean添加验证注解
@Data
public class TagAddForm {
@NotEmpty(message = "标签名称不能为空")
private String name;
@Min(value = 1,message = "标签类型不能为空")
private int type;
}
编写手动验证逻辑
@Component
public class TagValidate {
/**
* 验证bean的参数
* <p>
* 手动验证
* </p>
*/
public void validateBean(){
TagAddForm tagAddForm=new TagAddForm();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<TagAddForm>> violations = validator.validate(tagAddForm);
//为空代表验证通过
if (violations.size()==0){
return;
}
for(ConstraintViolation<TagAddForm> violation: violations) {
System.out.println((violation.getMessage()));
}
}
}
单元测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestValidate {
@Autowired
private TagValidate tagValidate;
@Autowired
private UserValidate userValidate;
@Test
public void test(){
tagValidate.validateBean();
}
}
结果:
自动验证
业务类编写
在类上添加注解:@Validated 在方法上添加:@Valid注解
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
@Validated
@Component
public class UserValidate {
public void add(@Valid TagAddForm tagAddForm){
System.out.println("添加标签");
}
}
测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestValidate {
@Autowired
private UserValidate userValidate;
@Test
public void test2(){
//new一个 TagAddForm对象作为参数并没有为其赋值
TagAddForm tagAddForm=new TagAddForm();
userValidate.add(tagAddForm);
}
}
结果
让自己变得更优秀才可以有更多资源