本文介绍项目中校验@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 fileInfoList;
// @NotEmpty会判断这个集合是不是空的,如果前端没传fileInfo这个参数,就不会校验里面的对象属性
// 只有加了这个注解,才能保证这个对象不能为空。
@Valid
@NotEmpty(message = "集合2不能为空")
private List 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 fileInfo) {
System.out.println("kaidsd");
System.out.println(fileInfo);
return "applyInfoDTO";
}
这里作为参数,如果使用List接收是不起作用的,必须用ValidList,这个类中有标记@Valid
@Valid
private List
如果在对象参数中使用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 fileInfo;
}
输入
还是上边输入
输出
{
"code": 400,
"message": "文件名称不能为空,文件名称不能为空",
"data": null,
"timestamp": 1679913439751
}
需要在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 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 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 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 handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
return R.fail(E.of(SystemExceptionEnum.METHOD_NOT_ALLOWED));
}
@ExceptionHandler({BusinessException.class})
public R 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 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 implements List {
@Valid
private List 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 iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public 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 listIterator() {
return list.listIterator();
}
@Override
public ListIterator listIterator(int index) {
return list.listIterator(index);
}
@Override
public List 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): [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层方法参数上加@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> 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层校验需要自己写校验工具类。写校验方法。