preface

We all know that when we write a controller, we need to check the request parameters at the back end, so we might write something like this

public String add(UserVO userVO) {
    if(userVO.getAge() == null) {return "Age cannot be empty.";
    }
    if(userVO.getAge() > 120) {return "No more than 120.";
    }
    if(userVO.getName().isEmpty()){
        return "User name cannot be empty";
    }
    // omit a bunch of parameter verification...
    return "OK";
}
Copy the code

Business code has not started to write, optical parameter verification to write a bunch of judgment. There’s nothing wrong with this, but it feels like: unelegant, unprofessional, unreadable code that looks like freshly written code

SpringBoot provides a validation solution that integrates the validation parameters with spring-boot-starter-validation

Integration of use

Versions prior to SpringBootv2.3 only needed to import web dependencies, which included the validation validation package. After that, the SpringBoot version became independent and needed to import its own dependencies

<! -- Parameter verification -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
Copy the code

There are many built-in validation annotations, listed as follows:

annotations Check the function
@AssertFalse Must be false
@AssertTrue It must be true
@DecimalMax Less than or equal to a given value
@DecimalMin Greater than or equal to a given value
@Digits Maximum number of integer and decimal digits can be set
@Email Verify that the Email format is correct
@Future It has to be in the future
@FutureOrPresent Present or future time
@Max The maximum
@Min The minimum value
@Negative Negative numbers (excluding 0)
@NegativeOrZero Negative or zero
@NotBlank Is not null and contains at least one non-whitespace character
@NotEmpty Not null and not null
@NotNull Not null
@Null null
@Past It has to be in the past
@PastOrPresent It has to be the past, including the present
@Pattern The regular expression must be satisfied
@PositiveOrZero Positive or zero
@Size Validates the number of elements in the container

Single parameter check

So it’s very simple to use it so you just put @Validated on the controller that you want to validate and you put @ null, @NotEmpty or something like that on the parameter that you want to validate,

@Validated
@GetMapping("/home")
public class ProductController {
  public Result index(@NotBlank String name, @Email @NotBlank String email) {
        returnResultResponse.success(); }}Copy the code

Object parameter verification

So it’s just a matter of annotating @validated, @validated, @NOtnull, @NOtempty, etc., on the properties of the object parameters that you want to validate,

 @PostMapping("/user")
public Result index1(@Validated @RequestBody UserParams userParams) {
        log.info("info test######");
        log.error("error test #####");
        return ResultResponse.success(userParams);
    }
Copy the code
@Data
public class UserParams {

    @NotBlank
    private String username;
    private int age;
    @NotBlank
    private String addr;
    @Email
    private String email;



}
Copy the code

Abnormal information processing of parameter verification

We conducted above parameter calibration, the default when parameter calibration after didn’t pass through the abnormal way to throw an error information is thrown when checking MethodArgumentNotValidException also includes many other abnormal information, then we can use the global capture information to deal with these parameters calibration anomaly

The global exception handling class simply needs to annotate @RestControllerAdvice on the class and specify which exception to handle with the @ExceptionHandler annotation on the method that handles the corresponding exception

package cn.soboys.core;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.soboys.core.authentication.AuthenticationException;
import cn.soboys.core.ret.Result;
import cn.soboys.core.ret.ResultCode;
import cn.soboys.core.ret.ResultResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
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.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/ * * *@author kenx
 * @version 1.0
 * @date2021/6/17 20:19 * Unified handling of global exceptions */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /** * Handle the exception thrown when the json request body calls the interface object parameter verification failure */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result jsonParamsException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List errorList = CollectionUtil.newArrayList();

        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            String msg = String.format("% s % s.", fieldError.getField(), fieldError.getDefaultMessage());
            errorList.add(msg);
        }
        return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList);
    }


    /** * Handle the exception thrown by a single parameter validation failure */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result ParamsException(ConstraintViolationException e) { List errorList = CollectionUtil.newArrayList(); Set<ConstraintViolation<? >> violations = e.getConstraintViolations();for(ConstraintViolation<? > violation : violations) { StringBuilder message =new StringBuilder();
            Path path = violation.getPropertyPath();
            String[] pathArr = StrUtil.splitToArray(path.toString(), ".");
            String msg = message.append(pathArr[1]).append(violation.getMessage()).toString();
            errorList.add(msg);
        }
        return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, errorList);
    }

    / * * *@param e
     * @returnHandle the exception */ thrown when the form data method fails to call the interface object parameter verification
    @ExceptionHandler(BindException.class)
    public Result formDaraParamsException(BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getField() + o.getDefaultMessage())
                .collect(Collectors.toList());
        return ResultResponse.failure(ResultCode.PARAMS_IS_INVALID, collect);
    }

    /** * The request method is not allowed to exception */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return ResultResponse.failure(ResultCode.METHOD_NOT_ALLOWED);
    }

    / * * *@param e
     * @returnContent-type /Accept * application/json * application/ x-w-form-urlencoded */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        return ResultResponse.failure(ResultCode.BAD_REQUEST);
    }

    /** * The handlerMapping interface does not run abnormally **@param e
     * @return* /
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result noHandlerFoundException(NoHandlerFoundException e) {
        return ResultResponse.failure(ResultCode.NOT_FOUND, e.getMessage());
    }


    /** * Authentication exception *@param e
     * @return* /
    @ExceptionHandler(AuthenticationException.class)
    public Result UnNoException(AuthenticationException e) {
        return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage());
    }

    / * * * *@paramE Unknown exception capture *@return* /
    @ExceptionHandler(Exception.class)
    public Result UnNoException(Exception e) {
        returnResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage()); }}Copy the code

For details about global exception handling, please refer to my article on elegant global exception handling in SpringBoot. Here I return a custom response body API. Please refer to my article on non-invasive Implementation RESTful API interface uniform JSON format

Of course, there are other ways to handle validation exceptions, which will help us to inject error messages into the BindingResult object if the parameter does not pass the validation, and into the corresponding Controller method, which only works if the parameter is @requestBody or @requestParam

public String add1(@Validated UserVO userVO, BindingResult result) {
    List<FieldError> fieldErrors = result.getFieldErrors();
    if(! fieldErrors.isEmpty()){return fieldErrors.get(0).getDefaultMessage();
    }
    return "OK";
}
Copy the code

Of course, I recommend the first way to uniformly handle verification exceptions through global exception handling

It would still be cumbersome to use if you wrote the BindingResult information in each Controller method. The code is redundant

When we have the @ “validated” annotation and no BindingResult, SpringBoot will throw an exception. As a result, you can write a global exception handling class to handle this validation exception uniformly, eliminating the need to repeatedly organize the exception information.

Pay attention to the public account ape life to get more dry goods to share