Why validator

  1. Javax. Validation has a set of annotations to help you validate parameters without the hassle of serial validation

    Otherwise our code would look something like this:

 // http://localhost:8080/api/user/save/serial

    /** ** Serial check **@param userVO
     * @return* /
    @PostMapping("/save/serial")
    public Object save(@RequestBody UserVO userVO) {
        String mobile = userVO.getMobile();

        // Manually check the ~ writing method one by one
        if (StringUtils.isBlank(mobile)) {
            return RspDTO.paramFail("Mobile: Mobile phone number cannot be empty");
        } else if(! Pattern.matches("^ [1] [3,4,5,6,7,8,9] [0-9] {9} $", mobile)) {
            return RspDTO.paramFail("Mobile: Wrong phone number format");
        }

        // Throw custom exceptions, etc
        if (StringUtils.isBlank(userVO.getUsername())) {
            throw new BizException(Constant.PARAM_FAIL_CODE, "User name cannot be empty");
        }

        // Write a map, for example
        if (StringUtils.isBlank(userVO.getSex())) {
            Map<String, Object> result = new HashMap<>(5);
            result.put("code", Constant.PARAM_FAIL_CODE);
            result.put("msg"."Gender cannot be empty.");
            return result;
        }
        / /... Various ways of writing...
        userService.save(userVO);
        return RspDTO.success();
    }
Copy the code

This is big guy see, certain say, all 9102 return so write, be persuaded to retreat next…..

  1. What is thejavax.validation

JSR303 is a standard for JavaBean parameter validation. It defines a number of common validation annotations that we can directly add to our JavaBean properties (annotation-oriented programming era). In SpringBoot, it is already included in the starter-Web, and in other projects it can reference dependencies and adjust the version itself:

<! --jsr 303--> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> < version > 1.1.0. Final < / version > < / dependency > <! -- hibernate validator--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> < version > 5.2.0. Final < / version > < / dependency >Copy the code

  1. Note that

    1.@NotNull: cannot be null, but can be empty(""," "," ") 2.@NotEmpty: cannot be null, and the length must be greater than 0 (""," ") 3.@NotBlank: This function applies only to strings, not null, and trim() must have a length greater than 0("test"), that is, must have real charactersCopy the code
Validation annotations The type of data to validate instructions
@AssertFalse Boolean,boolean Verify that the element value of the annotation is false
@AssertTrue Boolean,boolean Verify that the element value of the annotation is true
@NotNull Any type Verify that the element value of the annotation is not NULL
@Null Any type Verify that the element value of the annotation is NULL
@ Min (value = value) BigDecimal, BigInteger, byte,short, int, Long, and any Number or CharSequence (which stores numbers) subtypes Verify that the element value of the annotation is greater than or equal to the value specified by @min
@max (value= value) Same as @min Verify that the element value of the annotation is less than or equal to the value specified by @max
@ DecimalMin (value = value) Same as @min Verify that the element value of the annotation is greater than or equal to the value specified by @decimalmin
@ DecimalMax (value = value) Same as @min Verify that the element value of the annotation is less than or equal to the value specified by @decimalmax
@Digits(integer= integer number, fraction= decimal number) Same as @min Validates the upper limit of integer and decimal digits for the element value of the annotation
@size (min= lower limit, Max = upper limit) String, Collection, Map, array, etc Validates the element value of an annotation within the specified min and Max (inclusive) interval, such as character length and collection size
@Past java.util.Date,java.util.Calendar; The date type of the Joda Time class library Verify that the element value (date type) of the annotation is earlier than the current time
@Future Same as @past Verify that the element value (date type) of the annotation is later than the current time
@NotBlank CharSequence subtype Verify that the element value of the annotation is not null (not null, length 0 after removing the first whitespace), unlike @notempty, where @notBlank applies only to strings and removes the first whitespace of strings when comparing
@length (min= lower limit, Max = upper limit) CharSequence subtype Verify that the element value length of the annotation is within min and Max
@NotEmpty CharSequence subtype, Collection, Map, array Verify that the element value of the annotation is not null and is not empty (string length is not 0, collection size is not 0)
@range (min= min, Max = Max) BigDecimal, BigInteger, CharSequence, byte, short, int, long, such as atomic types and package types Verify that the element value of the annotation is between the minimum and maximum value
@email (regexp= regular expression,flag= pattern of flags) CharSequence subtype (e.g. String) Verify that the element value of the annotation is Email, or you can specify a custom Email format using regEXP and flag
@pattern (regexp= regular expression,flag= Pattern of flags) String, the subtype of any CharSequence Verifies that the element value of the annotation matches the specified regular expression
@Valid Any non-atomic type For example, if the user object has an address object attribute, if you want to validate the address object with the user object, add the @VALID annotation to the address object to cascade the authentication

Most of the validation constraint annotations provided by Hibernate Validator are listed here. Please refer to the Hibernate Validator official documentation for additional validation constraint annotations and custom validation constraint annotation definitions.

We practice

Without further words, go directly to the practical route, also using SpringBoot’s fast framework, see the detailed code :github.com/leaJone/myb…

1. @Validated Specifies the parameters to be checked

Here we make an annotation declaration at the controller layer

 / check note * * * * * go parameters@param userDTO
     * @return* /
    @PostMapping("/save/valid")
    public RspDTO save(@RequestBody @Validated UserDTO userDTO) {
        userService.save(userDTO);
        return RspDTO.success();
    }
Copy the code

2. Annotate the parameter fields


import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.Date;

/ * * *@author LiJing
 * @ClassName: UserDTO
 * @Description: User transfer object *@date2019/7/30 plague * /
@Data
public class UserDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    / * * * * / user ID
    @NotNull(message = "User ID cannot be empty")
    private Long userId;
    
    /** User name */
    @NotBlank(message = "User name cannot be empty")
    @Length(max = 20, message = "User name cannot exceed 20 characters.")
    @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "User nickname limit: up to 20 characters, including text, letters and numbers.")
    private String username;
    
    /** Mobile phone number */
    @NotBlank(message = "Cell phone number cannot be empty.")
    @Pattern(regexp = "^ [1] [3,4,5,6,7,8,9] [0-9] {9} $", message = "The phone number format is wrong.")
    private String mobile;

    / * * sex * /
    private String sex;

    / * * E-mail * /
    @NotBlank(message = "Contact email cannot be empty.")
    @Email(message = "Wrong email format")
    private String email;

    / * * * / passwords
    private String password;

    /*** create time */
    @Future(message = "The time must be future time.")
    private Date createTime;

}
Copy the code

3. Add verification exceptions to the global verification

MethodArgumentNotValidException is the exception in the case of binding parameters calibration springBoot, need in springBoot processing, need to deal with other ConstraintViolationException exception processing.

  • For the sake of elegance, we made a global exception by combining parameter exception and business exception, and wrapped the control layer exception into our custom exception
  • In order to be more elegant, we also made a unified structure, which encapsulated the requested code, MSG and data into the structure, increasing the reusability of the code

import com.boot.lea.mybot.dto.RspDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
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.ConstraintViolationException;
import javax.validation.ValidationException;


/ * * *@author LiJing
 * @ClassName: GlobalExceptionHandler
 * @Description: Global exception handler *@date2019/7/30 and * / appear
@RestControllerAdvice
public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private static int DUPLICATE_KEY_CODE = 1001;
    private static int PARAM_FAIL_CODE = 1002;
    private static int VALIDATION_CODE = 1003;

    /** * Handle custom exceptions */
    @ExceptionHandler(BizException.class)
    public RspDTO handleRRException(BizException e) {
        logger.error(e.getMessage(), e);
        return new RspDTO(e.getCode(), e.getMessage());
    }

    /** ** method parameter verification */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public RspDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        logger.error(e.getMessage(), e);
        return new RspDTO(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
    }

    /** * ValidationException */
    @ExceptionHandler(ValidationException.class)
    public RspDTO handleValidationException(ValidationException e) {
        logger.error(e.getMessage(), e);
        return new RspDTO(VALIDATION_CODE, e.getCause().getMessage());
    }

    /** * ConstraintViolationException */
    @ExceptionHandler(ConstraintViolationException.class)
    public RspDTO handleConstraintViolationException(ConstraintViolationException e) {
        logger.error(e.getMessage(), e);
        return new RspDTO(PARAM_FAIL_CODE, e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public RspDTO handlerNoFoundException(Exception e) {
        logger.error(e.getMessage(), e);
        return new RspDTO(404."Path does not exist, please check path is correct");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public RspDTO handleDuplicateKeyException(DuplicateKeyException e) {
        logger.error(e.getMessage(), e);
        return new RspDTO(DUPLICATE_KEY_CODE, "Duplicate data, please check and submit.");
    }


    @ExceptionHandler(Exception.class)
    public RspDTO handleException(Exception e) {
        logger.error(e.getMessage(), e);
        return new RspDTO(500."System busy, please try again later."); }}Copy the code

4. Test

It does return abnormal information and corresponding code when parameter verification, which is convenient for us to deal with parameter verification no longer

In ValidationMessages. The properties is to check the message, has already written the default message, and is the i18n support, you can read the source code

Custom parameter annotations

1. For example, let’s make a custom ID verification annotation

@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdentityCardNumberValidator.class)
public @interface IdentityCardNumber {

    String message(a) default"The ID number is illegal."; Class<? >[] groups()default {};

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

This annotation is applied to the Field Field at runtime and triggers the authentication class IdentityCardNumber.

  • Message customized, mainly from ValidationMessages. Extract, the properties can also be customized according to the actual situation
  • Groups Classifies validators into groups. Different groups perform different validators
  • Payload is mainly used for beans.

2. Then customize the Validator

Here’s the actual verification logic:

public class IdentityCardNumberValidator implements ConstraintValidator<IdentityCardNumber.Object> {

    @Override
    public void initialize(IdentityCardNumber identityCardNumber) {}@Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        returnIdCardValidatorUtils.isValidate18Idcard(o.toString()); }}Copy the code

IdCardValidatorUtils in the project source, you can view

3. Use custom annotations

    @NotBlank(message = "The ID number cannot be empty.")
    @IdentityCardNumber(message = "Id card information is wrong, please check and submit.")
    private String clientCardNo;
Copy the code

4. Use groups to verify

UserDTO = userId; userId = userId; username = groups; UserDTO = userId; UserDTO = userId;

Define the Create and Update group interfaces for groups

import javax.validation.groups.Default;

public interface Create extends Default {}import javax.validation.groups.Default;

public interface Update extends Default{}Copy the code

Then, declare the verification group at @, where the verification is required

 /** * check annotation groups group check **@param userDTO
     * @return* /
    @PostMapping("/update/groups")
    public RspDTO update(@RequestBody @Validated(Update.class) UserDTO userDTO) {
        userService.updateById(userDTO);
        return RspDTO.success();
    }
Copy the code

Define the group type of groups = {} on the field in the DTO

@Data
public class UserDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    / * * * * / user ID
    @NotNull(message = "User ID cannot be empty", groups = Update.class)
    private Long userId;

    /** * User name */
    @NotBlank(message = "User name cannot be empty")
    @Length(max = 20, message = "User name cannot exceed 20 characters.", groups = {Create.class, Update.class})
    @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "User nickname limit: up to 20 characters, including text, letters and numbers.")
    private String username;

    /** * Mobile phone number */
    @NotBlank(message = "Cell phone number cannot be empty.")
    @Pattern(regexp = "^ [1] [3,4,5,6,7,8,9] [0-9] {9} $", message = "The phone number format is wrong.", groups = {Create.class, Update.class})
    private String mobile;

    /** ** gender */
    private String sex;

    /** * email */
    @NotBlank(message = "Contact email cannot be empty.")
    @Email(message = "Wrong email format")
    private String email;

    /** * Password */
    private String password;

    /*** create time */
    @Future(message = "The time must be future time.", groups = {Create.class})
    private Date createTime;

}
Copy the code

Note: as far as possible when statement grouping and extend javax.mail. Validation. Groups. The Default or on your statement @ Validated (Update. Class), will appear in the Default didn’t you add groups = {} @email (message = “mailbox format is incorrect “) will not be checked because the Default verification group is {default.class}.

5. Restful Usage

When verifying multiple parameters or in the @RequestParam format, add @ “Validated” to the controller

 @GetMapping("/get")
    public RspDTO getUser(@RequestParam("userId") @NotNull(message = "User ID cannot be empty") Long userId) {
        User user = userService.selectById(userId);
        if (user == null) {
            return new RspDTO<User>().nonAbsent("User does not exist");
        }
        return new RspDTO<User>().success(user);
    }

Copy the code
@RestController
@RequestMapping("user/")
@Validated
public class UserController extends AbstractController {... Saint Lo code...Copy the code

conclusion

It is very simple to use. SoEasy, the return of unified structure with emphasis on participation, and unified parameter verification are the magic weapon to reduce a large number of try catch in our code. I think in the project, it is a good sublimation to handle exceptions well, and do a good job in log management of exceptions

Here is just personal opinion, technical dishes, welcome big guy to give advice… I am a small white, technology in the continuous update iteration, I only continue to fill their blank to…. Keep up with the big boys…