spring参数校验@Validated及嵌套校验
Posted wangfenglei123456
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring参数校验@Validated及嵌套校验相关的知识,希望对你有一定的参考价值。
本文介绍项目中校验@Validated的使用,主要分参数对象属性校验,嵌套校验,集合在对象属性中校验,集合作为参数校验。
对象属性校验
controller层
@RestController
@Slf4j
@RequestMapping("/api/test")
public class TestController
@PostMapping(value = "/h9")
public ApplyInfoDTO2 test9(@Validated @RequestBody ApplyInfoDTO2 applyInfoDTO)
System.out.println("kaidsd");
return applyInfoDTO;
package com.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApplyInfoDTO2 implements Serializable
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键")
private Long id;
@ApiModelProperty(value = "标题", required = true)
@NotBlank(message = "标题不能为空")
private String title;
@Valid
@NotEmpty(message = "集合1不能为空")
private List<FileInfoVO2> fileInfoList;
// @NotEmpty会判断这个集合是不是空的,如果前端没传fileInfo这个参数,就不会校验里面的对象属性
// 只有加了这个注解,才能保证这个对象不能为空。
@Valid
@NotEmpty(message = "集合2不能为空")
private List<FileInfoVO> fileInfo;
package com.common.vo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
public class FileInfoVO implements Serializable
@NotNull(message = "文件名称不能为空")
@ApiModelProperty("原文件名")
private String name;
测试输入
"titles": "44",
"fileInfoList": [
"name": "sss"
],
"fileInfo": [
"names": "sss"
]
输出
"code": 400,
"message": "标题不能为空,文件名称不能为空",
"data": null,
"timestamp": 1679912555257
集合作为参数校验
@PostMapping(value = "/h10")
public String test10(@Validated @RequestBody ValidList<FileInfoVO> fileInfo)
System.out.println("kaidsd");
System.out.println(fileInfo);
return "applyInfoDTO";
这里作为参数,如果使用List接收是不起作用的,必须用ValidList,这个类中有标记@Valid
@Valid
private List<E> list = new ArrayList<>();
如果在对象参数中使用ValidList,就会出现2次错误提示消息。
对象改为
package com.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApplyInfoDTO2 implements Serializable
// @NotEmpty会判断这个集合是不是空的,如果前端没传fileInfo这个参数,就不会校验里面的对象属性
// 只有加了这个注解,才能保证这个对象不能为空。
@Valid
@NotEmpty(message = "集合2不能为空")
private ValidList<FileInfoVO> fileInfo;
输入
还是上边输入
输出
"code": 400,
"message": "文件名称不能为空,文件名称不能为空",
"data": null,
"timestamp": 1679913439751
Get请求参数校验
需要在controller层上边加@Validated校验注解
正确形式
@RestController
@Slf4j
@RequestMapping("/api/test")
@Validated
public class TestController
@GetMapping(value = "/h11")
public String test11( @NotEmpty(message = "姓名不能为空") String name)
System.out.println("kaidsd");
System.out.println(name);
return "applyInfoDTO";
输入路径
http://localhost:9004/api/test/h11
后台报错
javax.validation.ConstraintViolationException: test11.name: 姓名不能为空
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
错误写法,不生效
@RestController
@Slf4j
@RequestMapping("/api/test")
// 这里没有校验注解,在参数前面加不生效
public class TestController
@GetMapping(value = "/h11")
public String test11(// 这里校验不生效 @Validated @NotEmpty(message = "姓名不能为空") String name)
System.out.println("kaidsd");
System.out.println(name);
return "applyInfoDTO";
报错信息捕捉
之所以上面的请求,校验不通过能返回错误信息,是因为设置了全局异常信息捕捉
import com.common.exception.BusinessException;
import com.common.exception.E;
import com.common.exception.SystemExceptionEnum;
import com.common.response.R;
import java.util.Iterator;
import java.util.Objects;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Order(0)
@RestControllerAdvice
public class SystemExceptionHandler
private static final Logger log = LoggerFactory.getLogger(SystemExceptionHandler.class);
private static final String ENUM_TYPE_ERROR_KEYWORD = "枚举类型错误";
public SystemExceptionHandler()
@ResponseBody
@ExceptionHandler(E.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handle(E e)
log.error("全局异常信息 -> ", e.getMessage(), e);
return e.isHideMessage() ? R.fail(e.getCode(), SystemExceptionEnum.UNKNOWN.getMessage()) : R.fail(e);
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> validationBodyException(MethodArgumentNotValidException e)
log.error("全局参数校验异常信息 -> ", e.getMessage(), e);
StringBuilder builder = new StringBuilder();
Iterator var3 = e.getBindingResult().getAllErrors().iterator();
while(var3.hasNext())
ObjectError error = (ObjectError)var3.next();
String defaultMessage = error.getDefaultMessage();
if (!Strings.isBlank(defaultMessage))
if (builder.length() > 0)
builder.append(",");
builder.append(defaultMessage);
String message = builder.toString();
message = Strings.isBlank(message) ? SystemExceptionEnum.ARGUMENT_ERROR.getMessage() : message;
return R.fail(SystemExceptionEnum.ARGUMENT_ERROR.getCode(), message);
@ResponseBody
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> httpMessageNotReadableException(HttpMessageNotReadableException e)
log.error("全局参数校验异常信息 -> ", e.getMessage(), e);
return ((String)Objects.requireNonNull(e.getMessage())).contains("枚举类型错误") ? R.fail(E.of(SystemExceptionEnum.ENUM_TYPE_ERROR)) : R.fail(E.of(SystemExceptionEnum.ARGUMENT_ERROR));
@ResponseBody
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public R<Void> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e)
return R.fail(E.of(SystemExceptionEnum.METHOD_NOT_ALLOWED));
@ExceptionHandler(BusinessException.class)
public R<Void> handleBusinessException(BusinessException e)
log.error("业务异常:,", e.getMessage(), e);
return R.fail(e.getCode(), e.getMessage());
@ResponseBody
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handleUnknown(Exception e)
log.error("全局未知异常信息 -> ", e.getMessage(), e);
return R.fail(E.of(SystemExceptionEnum.UNKNOWN));
自定义的ValidList类
package com.utils;
import lombok.Data;
import javax.validation.Valid;
import java.util.*;
/**
* 文件描述: 校验List中字段属性
*
* @date 2020年03月16日 11:22
*/
@Data
public class ValidList<E> implements List<E>
@Valid
private List<E> list = new ArrayList<>();
@Override
public int size()
return list.size();
@Override
public boolean isEmpty()
return list.isEmpty();
@Override
public boolean contains(Object o)
return list.contains(o);
@Override
public Iterator<E> iterator()
return list.iterator();
@Override
public Object[] toArray()
return list.toArray();
@Override
public <T> T[] toArray(T[] a)
return list.toArray(a);
@Override
public boolean add(E e)
return list.add(e);
@Override
public boolean remove(Object o)
return list.remove(o);
@Override
public boolean containsAll(Collection<?> c)
return list.containsAll(c);
@Override
public boolean addAll(Collection<? extends E> c)
return list.addAll(c);
@Override
public boolean addAll(int index, Collection<? extends E> c)
return list.addAll(index,c);
@Override
public boolean removeAll(Collection<?> c)
return list.removeAll(c);
@Override
public boolean retainAll(Collection<?> c)
return list.retainAll(c);
@Override
public void clear()
list.clear();
@Override
public E get(int index)
return list.get(index);
@Override
public E set(int index, E element)
return list.set(index,element);
@Override
public void add(int index, E element)
list.add(index,element);
@Override
public E remove(int index)
return list.remove(index);
@Override
public int indexOf(Object o)
return list.indexOf(o);
@Override
public int lastIndexOf(Object o)
return list.lastIndexOf(o);
@Override
public ListIterator<E> listIterator()
return list.listIterator();
@Override
public ListIterator<E> listIterator(int index)
return list.listIterator(index);
@Override
public List<E> subList(int fromIndex, int toIndex)
return list.subList(fromIndex,toIndex);
post请求报错异常信息类是MethodArgumentNotValidException
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.ccreate.cnpc.apply.controller.TestController.test10(com.ccreate.cnpc.apply.utils.ValidList<com.ccreate.cnpc.common.vo.FileInfoVO>): [Field error in object 'fileInfoVOList' on field 'list[0].name': rejected value [null]; codes [NotNull.fileInfoVOList.list[0].name,NotNull.fileInfoVOList.list.name,NotNull.list[0].name,NotNull.list.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [fileInfoVOList.list[0].name,list[0].name]; arguments []; default message [list[0].name]]; default message [文件名称不能为空]]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:139)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
get请求报错的是ConstraintViolationException
在service层校验对象参数
错误写法,在service层方法参数上加@Validated是不生效的
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@Service
// 加这不生效校验
@Validated
public class ApplyTestService
public Boolean add(// 加这也不生效校验 @Validated ApplyInfoDTO2 applyInfoDTO)
System.out.println("applyTestService");
return true;
正确写法
自定义一个校验工具类
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
import java.util.stream.Collectors;
public class ValidationUtils
private static final Validator validator;
static
validator = Validation.buildDefaultValidatorFactory().getValidator();
/**
* 校验对象
*
* @param object 待校验对象
* @param groups 待校验的组
*/
public static void validateEntity(Object object, Class<?>... groups) throws IllegalArgumentException
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty())
String msg = constraintViolations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("||"));
throw new E(msg);
正确service写法
import com.dto.ApplyInfoDTO2;
import com.ValidationUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@Service
public class ApplyTestService
public Boolean add(@Validated ApplyInfoDTO2 applyInfoDTO)
// 这里写自己的校验
ValidationUtils.validateEntity(applyInfoDTO);
System.out.println("applyTestService");
return true;
总结:
对象校验,用post传参,对象属性的校验注解导包使用正确
集合作为校验接收参数使用ValidList这个类接收
对象中集合校验使用List接收,不能使用ValidList,会出现2次错误信息
get参数校验,需要在类上加@Validated
service层校验需要自己写校验工具类。写校验方法。
Validated校验在springboot框架中的应用(教程版)
参考技术A各位朋友大家好,我是 奋斗的小强001 ,本期更新的内容是: Validated校验在springboot框架中的应用。
前言
4.1网上业务处理
4.2系统业务处理
当输入不能满足条件时,就会抛出异常,而后统一由异常中心处理, 推荐方式2
如果一个类中包含了另外一个实体类,那么在上面加上@Validated即可,比如下面的
优缺点
优点: 统一接口校验方法,并且可以指定相应的异常信息,避免手动写if else判断参数是否合法
缺点: 在公用一个打开校验的接口时,校验有问题.需要在使用的时候考虑
注意事项
1、修改时如果不传此校验的参数会报错,所以修改时也应该传全参。
2、使用时需在接口参数前添加注解开启此校验。
3、使用注解时应抛出异常信息。默认的异常信息是英文。
4、建议大家使用的时候先结合大家的业务使用.
以上是关于spring参数校验@Validated及嵌套校验的主要内容,如果未能解决你的问题,请参考以下文章
Spring Validation最佳实践及其实现原理,参数校验没那么简单!
优雅的参数验证@Validated@Validated参数校验的使用及注解详解——你还在用if做条件验证?
Validated校验在springboot框架中的应用(教程版)