Custom validation annotations based on Spring Validation

Commonly used native validation annotations are:

@NotNull All objects are judged as empty
@NotBlank String is judged as
empty @NotEmpty Collection is judged as empty

Implementation of custom validation annotations:

Introduce dependencies

If the spring-boot version is less than 2.3.x, spring-boot-starter-web will automatically pass in the hibernate-validator dependency. If the spring-boot version is greater than 2.3.x, you need to manually introduce dependencies:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.1.Final</version>
</dependency>

Define validation annotations

Customize a verification annotation, for string parameter verification, the verification logic is that the string must be "xzh".

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

/**
 * @author 向振华
 * @date 2022/11/23 16:52
 */
@Documented
@Constraint(validatedBy = {EqualsXzhValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface EqualsXzh {

    String message() default "必须是xzh";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };
}

Implement validation logic

The verification logic is that the string must be "xzh".

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * @author 向振华
 * @date 2022/11/23 16:56
 */
public class EqualsXzhValidator implements ConstraintValidator<EqualsXzh, String> {

    public EqualsXzhValidator() {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 自定义校验规则,字段的value必须是"xzh",否则抛异常
        return "xzh".equals(value);
    }
}

Global exception handling

Parameter verification will throw MethodArgumentNotValidException by default, usually use global exception capture and interception, and then return a prompt message.

import com.xzh.exception.BusinessException;
import com.xzh.web.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.StringJoiner;

/**
 * 全局异常捕获处理
 *
 * @author 向振华
 * @date 2022/11/23 16:52
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 异常处理程序
     *
     * @param request
     * @param exception
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public Object handler(HttpServletRequest request, Exception exception) {
        String errorLog = this.errorLog(request, exception);
        log.error("全局异常捕获日志 ---> {}", errorLog);

        String message;
        if (exception instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException e = (MethodArgumentNotValidException) exception;
            BindingResult bindingResult = e.getBindingResult();
            StringJoiner sj = new StringJoiner(",");
            for (FieldError fieldError : bindingResult.getFieldErrors()) {
                String longField = fieldError.getField();
                String shortField = longField.substring(longField.lastIndexOf(".") + 1);
                String defaultMessage = fieldError.getDefaultMessage();
                sj.add(shortField + defaultMessage);
            }
            message = sj.toString();
        } else if (exception instanceof BusinessException) {
            BusinessException e = (BusinessException) exception;
            message = e.getMessage();
        } else {
            message = "系统异常,请稍后重试";
        }

        return ApiResponse.fail(message);
    }


    private String errorLog(HttpServletRequest request, Exception exception) {
        StringWriter sw = new StringWriter();
        sw.append(String.format("[接口] %s\n", request.getRequestURL()));
        exception.printStackTrace(new PrintWriter(sw));
        return sw.toString();
    }
}

Parameter verification tool

 Parameter verification can also be verified using tool classes. If the verification fails, a business exception will be thrown (it will also be intercepted by global exception capture)

import com.xzh.exception.BusinessException;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
import java.util.StringJoiner;

/**
 * 参数校验工具
 *
 * @author 向振华
 * @date 2022/11/23 16:52
 */
public class ValidatedUtils {

    private static final Validator VALIDATOR;

    static {
        VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
    }

    private ValidatedUtils() {
    }

    public static void verify(Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> constraintViolations = VALIDATOR.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            StringJoiner sj = new StringJoiner(",");
            for (ConstraintViolation<Object> constraint : constraintViolations) {
                sj.add(constraint.getPropertyPath().toString() + constraint.getMessage());
            }
            throw new BusinessException(sj.toString());
        }
    }
}

use

Just add the @EqualsXzh annotation on the parameter

@EqualsXzh
private String name;

If it is a nested validation, you need to add @Valid

@Data
public class OrderDTO {

    @EqualsXzh
    private String name;

    @Valid
    private AccountDTO account;

    @Valid
    private List<OrderItemDTO> orderItemList;
}

Add @Validated annotation before the object

@PostMapping("/test")
public ApiResponse<String> test(@RequestBody @Validated OrderDTO dto) {
    // TODO
    return ApiResponse.success();
}

If the verification fails, a prompt will appear: name must be xzh

Among them, BusinessException is a custom business exception, and ApiResponse is a custom general return format.

Replenish

Previous Validation Notes Articles

Integration of Spring Boot global exception handling and Hibernate Validator verification framework - Programmer Sought

Use of Spring Validation Framework @Validated_ Grasper's Blog - CSDN Blog

 

Guess you like

Origin blog.csdn.net/Anenan/article/details/128004111