Introduction to the

In the Java backend development work, some verification is often done on the parameters passed from the front end. In the business, the verification information when exceptions are thrown or continuously returned is full of if-else verification code, which is quite lengthy in the code. For example, when a user registers, it verifies the correct format of the phone, the length of the user name, and so on. Although the front end can also do parameter verification, in order to ensure the reliability of our API interface and the correctness of the final data into the library, the parameter verification of the back end can not be ignored. Hibernate Validator provides a unified and convenient way to quickly implement parameter validation.

Hibernate ValidatorUse annotations to implement declarative validation. In the implementation principle, it is also based on Spring AOP interception to achieve verification related operations.javax.validation.constraintsPackage, defines a set of constraint annotations. Some common notes will be attached at the end of the article. If the project framework is SpringBoot, inspring-boot-starter-webIs already covered inHibernate-validatorThe dependence of (Version < 2.3).

Spring-boot-starter-web removes this dependency and requires manual introduction of the Hibernate-Validator dependency. This article has been upgraded to version 2.3.3.

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

The full code has been uploaded to GitHub: github.com/yese777/val…

RequestBodyParameter calibration

Create an entity class

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.*;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
    /** * user ID */
    @notnull (message = "user ID cannot be empty ")
    private Long userId;

    /** * User name */
    @notblank (message = "user name cannot be blank ")
    @length (Max = 20, message = "user name cannot exceed 10 characters ")
    @pattern (regexp = "^[\\ e00 -\\ u9FA5A-za-z0-9 \\*]*$", message = "Username limit: up to 10 characters including text, letters and numbers ")
    private String username;

    /** * Password */
    @notblank (message = "password cannot be blank ")
    private String password;

    /** * Mobile phone number */
    @notblank (message = "Phone number cannot be blank ")
    ^ @ the Pattern (regexp = "[1] [3,4,5,6,7,8,9] [0-9] {9} $", message =" phone number format is wrong ")
    private String mobile;

    /** ** gender */
    @notnull (message = "Gender cannot be empty ")
    private Integer sex;

    /** * age */
    @notnull (message = "Age cannot be empty ")
    @min (value = 1, message = "age minimum 1 ")
    @max (value = 120, message = "Max 120 years old ")
    private Integer age;

    /** * email */
    @notblank (message = "mailbox cannot be empty ")
    @email (message = "mailbox format error ")
    private String email;

}
Copy the code

2. Create controller

import com.yese.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class UserController {

    /** * the controller method input parameter before the entity class@Validated* /
    @PostMapping("/user")
    public void add(@RequestBody @Validated User user) {
        log.info("user====={}", user); }}Copy the code

Create a global exception handler

import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

/** * Global exception handler */
@RestControllerAdvice
public class GlobalExceptionHandler {
    / * * * send post json data, parameter calibration failed, throws a MethodArgumentNotValidException * /
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        // Get all errors
        // List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        // Get an error message
        // System.out.println(fieldErrors.get(0).getDefaultMessage());
        // Get the error field
        // System.out.println(fieldErrors.get(0).getField());

        // Use ";" for all error messages. Splice and return
        StringJoiner sj = new StringJoiner(";");
        e.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));

        // A global corresponding object is usually defined here
        Map<String, Object> map = new HashMap<>();
        // The status code can be defined by enumeration or constants
        map.put("code".1001);
        map.put("msg", sj.toString());
        return map;
    }

    / * * * get submitted parameters, check failed, throws a ConstraintViolationException * /
    @ExceptionHandler(ConstraintViolationException.class)
    public Map<String, Object> handleConstraintViolationException(ConstraintViolationException e) {
        StringJoiner sj = new StringJoiner(";");
        e.getConstraintViolations().forEach(x -> sj.add(x.getMessage()));

        Map<String, Object> map = new HashMap<>();
        map.put("code".1001);
        map.put("msg", sj.toString());
        returnmap; }}Copy the code

4, test,

The effect is as expected!

RequestParam/PathVariableParameter calibration

    /** * add:@Validated
     * @RequestParamMode bind parameter */
    @GetMapping("/get1")
    public void get1(@RequestParam("id") @notnull (message = "ID cannot be empty ") Integer id,
                     @RequestParam("name") @notblank (message = "name cannot be empty ") @length (Max = 10, message = "user name cannot exceed 10 characters ") String name) {
        log.info("Id: {}, name: {}", id, name);
    }

    / * * *@PathVariableMode bind parameter */
    @GetMapping("/get2/{id}")
    public void get2(@PathVariable("id") @min (value = 0, message = "id minimum 0") Integer id) {
        log.info("Id: {}", id);
    }
Copy the code

@ Valid and @ Validated

In most cases, we’ll just use the @ “Validated” notation.

In the case of nested validations, we use the @Valid annotation to add to the member property. Reference:

public class Dog {
    @notblank (message = "dog name cannot be blank ")
    private String name;
}
Copy the code
public class User {
	// omit other...

    // For nested verification
    @Valid
    private Dog dog;
}
Copy the code

Define prompt messages centrally

In the resources directory new ValidationMessages. The properties files, content is as follows:

id.NotNull=Id cannot be empty
username.NotBlank=The user name cannot be empty
username.length=The user name cannot exceed 10 characters
username.invalid=User name: contains a maximum of 10 characters, including characters, letters, and digits
password.NotBlank=The password cannot be empty
phone.NotBlank=Mobile phone number cannot be empty
phone.invalid=The phone number format is invalid
sex.NotNull=Gender cannot be empty
age.NotNull=Age cannot be empty
age.min=The minimum age is 1 year
age.max=The maximum age is 120 years
email.NotBlank=The mailbox cannot be empty
email.invalid=The email format is invalid
dogName.NotBlank=Dog names cannot be blank
Copy the code

Modify the original validation annotations on the text, use a placeholder {key} obtaining ValidationMessages. The properties defined in the text. To define additional prompts later, you simply need to centrally define them in the properties and reuse them. For reference:

Packet check

The groups function is used when the same object is to be reused, for example, the userId is verified when the User is updated, but the userId is not verified when the User is added, and other parameters are verified in both cases. Define two interfaces:

import javax.validation.groups.Default;

/** * used for grouping verification when adding */
public interface Insert extends Default {}Copy the code
import javax.validation.groups.Default;

/** * used for grouping verification during update */
public interface Update extends Default {}Copy the code

Modify entities. If all other parameters need to be authenticated, you can leave groups alone.

public class User {
    /** * user ID */
    @NotNull(message = "{id.NotNull}", groups = {Update.class})
    private Long userId;
    
    /** * User name */
    @NotBlank(message = "{username.NotBlank}", groups = {Insert.class, Update.class})
    @Length(max = 10, message = "{username.length}")
    @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "{username.invalid}")
    private String username;
}
Copy the code

The Groups array indicates the range in effect. Modify controller methods

    @PostMapping("/user")
    public void add(@RequestBody @Validated(Insert.class) User user) {
        log.info("user====={}", user);
    }
Copy the code

@Validated(Insert. Class) indicates that this validation only verifies that groups contains the Insert. Class parameter. That is, the userId is not validated in the current case. Both cases are checked if the entity’s other parameters do not explicitly define groups.

When the id is added, it is not verified, and the effect is as follows:

If the value is changed to @Validated(update.class)

When update, check id, effect is as follows:

Custom validation annotations

1. Custom constraint annotations

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

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

    // Default error message
    String message(a) default"Username is not valid";

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

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

}
Copy the code

2. Implement ConstraintValidator interface to write ConstraintValidator

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

/** * User-defined user name authentication rule * The user name must start with ABC */
public class UsernameValidator implements ConstraintValidator<Username.String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // Verify only when the value is not null
        if(value ! =null) {
            return value.startsWith("abc");
        }
        return true; }}Copy the code

Use 3.

    @notblank (message = "name cannot be empty ")
    @username (message = "name must start with ABC ")
    private String name;
Copy the code

With common notes

Javax.mail. Validation. Constraints under the package

annotations The type of data to validate instructions
@Null Any type The element value of the validation annotation must be NULL
@NotNull Any type Verify that the element value of the annotation is not NULL
@NotBlank string Verify that the element value of the annotation is not null (not null, length greater than 0 after removing the first whitespace)
@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)
@ Min (value = value) Numeric types 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
@Positive Judged positive
@PositiveOrZero Determine a positive number or 0.
@Negative Judge the negative numbers.
@NegativeOrZero Judge negative numbers or 0.
@Past The date type 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
@AssertFalse Boolean Verify that the element value of the annotation is false
@AssertTrue Boolean Verify that the element value of the annotation is true
@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 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 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

. Org. Hibernate. The validator constraints under the package, defines a series of constraints (constraint) annotations. As follows:

@range (min=, Max =) : Annotated elements must be in the appropriate Range.

@length (min=, Max =) : The size of the annotated string must be within the specified range.

@URL(protocol=,host=,port=,regexp=,flags=) : The annotated string must be a valid URL.

SafeHtml: Determine if the submitted HTML is safe. For example, you can’t include javascript scripts and so on.