This is the sixth day of my participation in the August More text Challenge. For details, see:August is more challenging

In daily API development, for the security of the application and user experience, we are inseparable from API input parameter verification, we can use Hibernate Validator to verify parameters. When the built-in validation rules cannot meet our verification needs, we can use custom annotations to implement parameter verification with Hibernate Validator.

A, Hibernate Validator

1. Common verification annotations

The elements of the @AssertFalse annotation must be of type Boolean and have a value of False

The elements of the @AssertTrue annotation must be of type Boolean and have a value of true

The elements of the @DecimalMax annotation must be numeric and have a value less than or equal to the given value

The elements of the @Decimalmin annotation must be numeric and have a value greater than or equal to the given value

The element for the @digits annotation must be a number, and the value must be the number of Digits specified

The elements of the @max annotation must be numeric and the value must be less than or equal to the given value

The elements of the @min annotation must be numeric and the value must be less than or equal to the given value

Elements in the @range annotation must be within the specified Range

The element for the @Future annotation must be a date in the Future

The element in the @past annotation must be a date in the Past

The element in the @pastorpresent annotation must be a PastOrPresent date

The element value of the @notnull annotation cannot be null

The @notblank annotation element value has content (non-null, length 0 after removing the first space) that is used for strings

The @NULL annotation has an element value of Null

Elements of the @pattern annotation must satisfy the given regular expression

The element of the @size annotation must be a String, collection, or array within the given range

The elements of the @email annotation must be in the Email format

2. Validator usage

A. Verify RequestParam parameters

Note: When validating RequestParam, the @validated annotation must be used on the controller class, otherwise the validation annotation will not take effect.

  • Error demonstration:
  @RestController
  @RequestMapping("test")
  public class TestController {
      @GetMapping("users")
      public void userList(@Validated @min (value = 1, message = "page no less than 1") @RequestParam(name = "page", defaultValue = "1") Integer page) { System.out.println(page); }}Copy the code
  • What to do:
  @RestController
  @RequestMapping("test")
  @Validated
  public class TestController {
      @GetMapping("users")
      public void userList(@min (value = 1, message = "page no less than 1") @RequestParam(name = "page", defaultValue = "1") Integer page) { System.out.println(page); }}Copy the code

In the console, we can see the exception thrown when the parameter fails to pass:

Javax.mail. Validation. ConstraintViolationException: userList. Page: the page number cannot be less than 1Copy the code

{“code”: 400, MSG: “page number cannot be less than 1”, “data”: null}, with a uniform return format.

  • Create a unified response class with the following code:
  public class UnifyResponse<T> {
    	private int code;
      private String msg;
      private T data;
    
    	public UnifyResponse (int code, String message) {
          this.code = code;
          this.msg = message;
          this.request = request;
          this.data = null;
      }
      public UnifyResponse (int code, String message, T data) {
            this.code = code;
            this.msg = message;
            this.request = request;
            this.data = null; }}Copy the code
  • use@ControllerAdviceAnnotation to achieve parameter verification exception processing:
  @ControllerAdvice
  public class GlobalExceptionAdvice {
      / / ConstraintViolationException parameter validation exception handling
      @ExceptionHandler(ConstraintViolationException.class)
    	// The response HTTP status code is 400
      @ResponseStatus(code = HttpStatus.BAD_REQUEST)
      @ResponseBody
      public UnifyResponse handleConstraintException (HttpServletRequest request, ConstraintViolationException e) {
        	// Get the exception information of parameter verification, annotated message
          String errorMsg = e.getConstraintViolations().iterator().next().getMessage();
          return new UnifyResponse(400, errorMsg); }}Copy the code
  • It responds when parameter verification fails
  {
    "code": 40009."msg": "Page no less than 1"."data": null
  }
Copy the code

B. RequestBody check

In complex requests, we could create beans to receive these parameters, and we could use validation annotations on each member variable that needs validation. For example, there is a requirement to add a user, and the fields are username and password.

Note: Bean validation must be preceded by an @Validated annotation, otherwise the validator will not take effect.

  • createbean, add the verification rule:usernameThis parameter is mandatory and is 2 to 50 characters long.passwordThis parameter is mandatory and contains 6 to 20 characters.
  @Data
  class User {
    @notblank (message = "User name required ")
    @length (min = 2, Max = 20, message = "username 2-50 ")
    private String username;
    @notblank (message = "password required ")
    @length (min = 6, Max = 20, message = "password Length 6-20")
    private String password;
  }
Copy the code
  • The controller
  @RestController
  @RequestMapping("test")
  @Validated
  public class TestController {
    @PostMapping("users")
      public void createUser (@Validated @RequestBody User user) { System.out.println(user); }}Copy the code
  • Thrown when a parameter fails to pass the checkorg.springframework.web.bind.MethodArgumentNotValidExceptionAnomaly, we continue inGlobalExceptionAdviceAdded the handling of this exception in:
  @ControllerAdvice
  public class GlobalExceptionAdvice {...@ExceptionHandler(MethodArgumentNotValidException.class)
      @ResponseStatus(code = HttpStatus.BAD_REQUEST)
      @ResponseBody
      public UnifyResponse handleBeanValidation (HttpServletRequest request, MethodArgumentNotValidException e) {
          List<ObjectError> errors = e.getBindingResult().getAllErrors();
          String message = errors.get(0).getDefaultMessage();
          return new UnifyResponse(40009, message); }}Copy the code

Two, custom verification annotation

When Hibernate Validator does not provide the validation annotation for our parameter validation needs, we need to customize the validation annotation. We need to add a field “mobile”, so we can customize a note to verify the number of the mobile phone.

  • New annotation, pass@ConstraintTell the validator which validator to use.
  @Retention(RetentionPolicy.RUNTIME)
  @Target({ElementType.FIELD, ElementType.PARAMETER})
  @Constraint(validatedBy = MobileNumberValidator.class)
  public @interface MobileNumber {
    	// Annotate parameters and check failure message
      String message(a) default"Incorrect cell phone number.";
    	// Specify whether this parameter is mandatory
      boolean require(a) default false; Class<? >[] groups()default {};
  
      Class<? extends Payload>[] payload() default {};
  }
Copy the code
  • Annotation validator implementation class MobileNumberValidator; To implement the ConstraintValidator interface, the first item in the generic type is the validation annotation and the second item is the data type of the field to be verified.

    Implement the initialize and isValid methods. In initialize, we can get the annotation and get some parameters in the annotation. IsValid is the actual verification method of the parameter. If true is returned, the verification succeeds. Otherwise, the verification fails.

  public class MobileNumberValidator implements ConstraintValidator<MobileNumber.String> {
    	private boolean require;
  
      @Override
      public void initialize(MobileNumber constraintAnnotation) {
          this.require = constraintAnnotation.require();
      }
    
    	@Override
      public boolean isValid(String mobile, ConstraintValidatorContext constraintValidatorContext) {
          if (this.require) {
              return! StringUtils.isBlank(mobile) &&this.isMobileNumber(mobile);
          } else {
              return StringUtils.isBlank(mobile) || this.isMobileNumber(mobile); }}private boolean isMobileNumber (String mobile) {
          String regex = "^1[3-9]\\d{9}$";
          if(mobile.length() ! =11) {
              return false;
          } else {
              Pattern p = Pattern.compile(regex);
              Matcher matcher = p.matcher(mobile);
              returnmatcher.matches(); }}}Copy the code
  • Usage: Add the @mobilenumber annotation to the field. If required, enter require = true. You can use the message parameter to define the parameter verification failure message.

    • bean
  @Data
  class User {
    @notblank (message = "User name required ")
    @length (min = 2, Max = 20, message = "username 2-50 ")
    private String username;
    @notblank (message = "password required ")
    @length (min = 6, Max = 20, message = "password Length 6-20")
    private String password;
    @mobilenumber (require = true, message = "MobileNumber verification failed ")
    private String mobile;
  }
Copy the code
  • controller
  @RestController
  @RequestMapping("test")
  @Validated
  public class TestController {
      @GetMapping("users")
      public void userList(
        @min (value = 1, message = "page no less than 1") @RequestParam(name = "page", defaultValue = "1") Integer page, 
        @MobileNumber @RequestParam(name = "mobile", defaultValue = "") String mobile) { System.out.println(page); System.out.println(mobile); }}Copy the code