使用@Constraint和自定义注解校验接口入参

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;
    }


}

附上完整的源码

码云地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值