1.一般的参数校验怎么做的?
我们在POST请求接受一个对象参数的时候可以使用 @Valid 去验证,然后通过统一异常处理,直接返回给前端,不用在业务代码中对这些参数进行校验。
且约束的类型也有很多,比如:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
上面注解实现的约束是spring官方给我们定义好的,好处就是可以直接拿来使用,坏处就是我们自己无法修改,当不满足需求时无法再使用。
2.更加优雅的自定义注解验证接口入参方案
更加优雅的检验方式,我们一般需要结合hibernate-validator包提供的@Constraint这个官方注解,首先我们需要定义了一个自定义注解,然后该注解的逻辑使用@Constraint这个注解帮我们处理
首先引入依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
编写自己的自定义注解校验逻辑
package com.example.demo.annotation;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author ghl
* @version 1.0
* @date 2022/9/21 10:17
* @description 测试实体
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Constraint(validatedBy = ColumnLengthValid.ColumnLengthValidator.class)
@Inherited
public @interface ColumnLengthValid {
int maxLength() default 0;
String message() default "字段超长!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
class ColumnLengthValidator implements ConstraintValidator<ColumnLengthValid, Object> {
private int maxLength;
@Override
public void initialize(ColumnLengthValid constraintAnnotation) {
maxLength = constraintAnnotation.maxLength();
}
@Override
public boolean isValid(Object obj, ConstraintValidatorContext constraintValidatorContext) {
return obj == null || obj.toString().length() <= maxLength;
}
}
}
使用方式
package com.example.demo.dto;
import com.example.demo.annotation.ColumnLengthValid;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @author ghl
* @version 1.0
* @date 2022/9/21 10:17
* @description 测试实体
*/
public class Person implements Serializable {
@ColumnLengthValid(maxLength = 5, message = "名称超长")
@NotBlank(message = "名称不能为空")
private String name;
@NotBlank(message = "地址不能为空")
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
重写Validated验证List集合入参
package com.example.demo.other;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* @author ghl
* @version 1.0.0
* @description 重写Validated验证List集合入参
* @date 2022-01-12 10:32:49
*/
public class ValidList<E> implements List<E> {
@Valid
private List<E> list = new ArrayList<>();
public List<E> getList() {
return list;
}
public void setList(List<E> list) {
this.list = list;
}
@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);
}
}
封装全局异常处理
package com.example.demo.exception;
import com.example.demo.core.ResultDTO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.List;
import java.util.Set;
/**
* @author ghl
* @version 1.0
* @date 2022/9/21 12:14
* @description 全局异常处理
*/
@RestControllerAdvice
public class BaseExceptionHandle {
private static final Logger logger = LoggerFactory.getLogger(BaseExceptionHandle.class);
/**
* 统一处理请求参数校验(实体对象传参)
*
* @param e BindException
* @return FebsResponse
*/
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResultDTO handleBindException(BindException e) {
StringBuilder message = new StringBuilder();
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
for (FieldError error : fieldErrors) {
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
logger.error(message.toString());
ResultDTO<String> resultDTO = new ResultDTO<String>();
resultDTO.setSuccess(false);
resultDTO.setMessage(e.getMessage());
return resultDTO;
}
/**
* 统一处理请求参数校验(普通传参)
*
* @param e ConstraintViolationException
* @return FebsResponse
*/
@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResultDTO handleConstraintViolationException(ConstraintViolationException e) {
StringBuilder message = new StringBuilder();
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
for (ConstraintViolation<?> violation : violations) {
Path path = violation.getPropertyPath();
String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), ".");
message.append(pathArr[1]).append(violation.getMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
logger.error(message.toString());
ResultDTO<String> resultDTO = new ResultDTO<String>();
resultDTO.setSuccess(false);
resultDTO.setMessage(message.toString());
return resultDTO;
}
/**
* 统一处理请求参数校验(json)
*
* @param e MethodArgumentNotValidException
* @return FebsResponse
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResultDTO handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {
logger.error(e.getMessage(), e);
StringBuilder message = new StringBuilder();
for (FieldError error : e.getBindingResult().getFieldErrors()) {
String defaultMessage = error.getDefaultMessage();
if (!message.toString().contains(defaultMessage)) {
message.append(error.getDefaultMessage()).append("!");
}
}
logger.error(message.toString());
ResultDTO<String> resultDTO = new ResultDTO<String>();
resultDTO.setSuccess(false);
resultDTO.setMessage(message.toString());
return resultDTO;
}
@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResultDTO handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
String message = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "媒体类型";
logger.error(message);
ResultDTO<String> resultDTO = new ResultDTO<String>();
resultDTO.setSuccess(false);
resultDTO.setMessage(message.toString());
return resultDTO;
}
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResultDTO handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
String message = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "请求方法";
logger.error(message);
ResultDTO<String> resultDTO = new ResultDTO<String>();
resultDTO.setSuccess(false);
resultDTO.setMessage(message.toString());
return resultDTO;
}
}