Use rear spring validation for data validation - custom validation annotation - determines whether the air

The introduction of dependence

We use maven build springboot demo application to demonstrate.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

We only need to introduce the spring-boot-starter-web can rely on, to see if his son dependent, can be found in the following dependence:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

Verify I described before, web module uses hibernate-validation, and databind module also provides a corresponding data binding.

Construction of startup class

No need to add additional notes, a typical startup class

@SpringBootApplication
public class TestSpringApplication{
 
    public static void main(String[] args) {
        SpringApplication.run(TestSpringApplication.class, args);
    }
}

Creating an entity class needs to be verified

public  class Foo { 
 
    @NotBlank 
    Private String name; 
 
    @min ( 18 is )
     Private Integer Age; 
 
    @Pattern (regexp = "^. 1 (. 3 |. 4 |. 5 |. 7 |. 8). 9 \\ {D} $", Message = " phone number format error " ) 
    @NotBlank (the Message =" phone number can not be empty " )
     Private String phone; 
 
    @email (the Message =" E-mail format error " )
     Private String Email; 
 
    // ... getter setter 
 
}

Some of the more common use of check notes, it is quite easy to understand, and comment on the name of the field to infer check the contents of each comment contains a message field for a message when the check fails, a special check annotations, such as the Pattern (regular check), you can also add your own regular expressions.

In the check data @Controller

FIG springmvc automatic packaging function is provided for us form parameters, a typical add parameter validation controller as follows.

@Controller
public class FooController {
@RequestMapping(
"/foo") public String foo(@Validated Foo foo <1>, BindingResult bindingResult <2>) { if(bindingResult.hasErrors()){ FieldError fieldError = (FieldError) bindingResult.getAllErrors().get(0); return fieldError.getDefaultMessage(); } return "success"; }
}

Noteworthy:

  • You need to add @Validated remarks before the parameter Foo, the need spring to validate it, and to verify the information will be stored in the subsequent BindingResult. Note that, you must be adjacent, if there are multiple parameters to be verified, the form may be as follows. foo (@Validated Foo foo, BindingResult fooBindingResult, @ Validated Bar bar, BindingResult barBindingResult); i.e., a class corresponding to a parity check result.
  • Check result will be automatically filled, the controller can be determined in specific operations such as a jump to the error page based on business logic.

A basic check is complete, summarize what has been provided under the framework of verification:

Check annotations JSR provides:          
@null annotated element must be null     
@NotNull annotated element must not be null     
@AssertTrue annotated element must be to true     
@AssertFalse annotated element must be false     
@min (value) is element annotation must be a number whose value must be greater than the specified minimum     
@Max (value) annotated element must be a number whose value is equal to the specified maximum value must be less than     
@DecimalMin (value) of the element must be annotated is a number whose value must be greater than the specified minimum     
@DecimalMax (value) annotated element must be a number whose value is equal to the specified maximum value must be less than     
@Size (max =, = min ) annotated elements size must be within the specified range     
@Digits (integer, fraction) annotated element must be a number whose value must be within an acceptable range     
@Past annotated element must be a date in the past     
@Future element annotated must be a future date     
@Pattern (regex =, Flag = ) annotated element must match the specified regular expression    
 
Hibernate Validator check annotations provided:   
@NotBlank (Message = ) authentication string is not null, and the length must be greater than 0     
@email elements must be annotated electronic mail address     
@Length (min =, max = ) string annotated the size must be within the specified range     
@NotEmpty string is non-empty comment must     
@range (min =, max =, = Message) element to be annotated in the range of appropriate

Check experiment

We achieved above the entrance to check a test request: 
visit  http: // localhost: 8080 / foo name = test & email = 000 & age = 19?  Get the following debug information:

Experiment tells us, check the results played a role. And can be found when multiple errors occur, spring validation does not stop immediately after the first error occurred, but continued trial and error, tell us all wrong. debug can view more rich error information, which are spring convenience features validation provides us with basic for most scenes.

You may not be satisfied to simply verify features, following some supplements.

The packet checksum

If there are different classes of the same validation rules in different usage scenarios, you can use the packet checksum. Minors are not drinking, while in other scenarios we do not particularly limited, and the demand reflects how the same entity, different validation rules?

Rewrite the notes, add groups:

Class Foo{
 
    @Min(value = 18,groups = {Adult.class})
    private Integer age;
 
    public interface Adult{}
 
    public interface Minor{}
}

This shows that only under the Adult group, 18-year-old restrictions will work.

Controller layer rewrite:

@RequestMapping("/drink")
public String drink(@Validated({Foo.Adult.class}) Foo foo, BindingResult bindingResult) {
    if(bindingResult.hasErrors()){
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            //...
        }
        return "fail";
    }
    return "success";
}
 
@RequestMapping("/live")
public String live(@Validated Foo foo, BindingResult bindingResult) {
    if(bindingResult.hasErrors()){
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            //...
        }
        return "fail";
    }
    return "success";
}

The method defined Adult drink required verification, the method is not limited in live.

Custom check

These simple check is always better than the business requirements framework provides much more complex, we can customize the check to meet our needs. Custom spring validation is very simple, divided into two steps.

1 Custom check notes 
we try to add a restriction "string can not contain spaces" of.

@Target ({the METHOD, the FIELD, ANNOTATION_TYPE, CONSTRUCTOR, the PARAMETER}) 
@Retention (RUNTIME) 
@Documented 
@Constraint (validatedBy = {CannotHaveBlankValidator. Class }) <. 1>
 public @ interface CannotHaveBlank { 
 
    // default error message 
    String message () default "not contain spaces" ; 
 
    // packet 
    Class [] groups () <?> default {}; 
 
    // load 
    Class <? the extends [] payload () payload> default {}; 
 
    // specified using a plurality 
    @Target ({the FIELD, the METHOD, the PARAMETER, ANNOTATION_TYPE})  
    @Retention (RUNTIME)
    @Documented
    @interface List {
        CannotHaveBlank[] value();
    }
 
}

We do not focus on too many things, using the principle of spring validation of our development is convenient, such as payload, List, groups, can be ignored.

  1.  Custom annotation specified in this annotation real verifier class.
  2.  Write real verifier class
public class CannotHaveBlankValidator implements <1> ConstraintValidator<CannotHaveBlank, String> {
 
    @Override
    public void initialize(CannotHaveBlank constraintAnnotation) {
} @Override
public boolean isValid(String value, ConstraintValidatorContext context <2>) { //null时不进行校验 if (value != null && value.contains(" ")) { <3> //获取默认提示信息 String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate(); System.out.println ( "default message :" +defaultConstraintMessageTemplate); // disable the default message context.disableDefaultConstraintViolation (); // set the prompt context.buildConstraintViolationWithTemplate ( "CAN not the contains blank" ) .addConstraintViolation (); return false ; } return to true ; } }

All certifiers are required to implement ConstraintValidator the interface, its interface is also very image, it contains an event initialization method, and a method to determine the legality of.

public interface ConstraintValidator<A extends Annotation, T> {
 
    void initialize(A constraintAnnotation);
 
    boolean isValid(T value, ConstraintValidatorContext context);
}

intValidatorContext this context contains all the information on certification, we can use this context to achieve access to the default error message, disable the error message, rewrite error messages and other operations.
Some typical verify operation, may be able to generate inspiration to you.

A point worth noting is that the custom annotations can be used on METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, ConstraintValidator second generic parameter T, is the type that needs to be verified.

Manual check

In some scenarios it may require us to manually check that the use validator to check the entity that needs to be initiated validate, verify synchronization obtain results. Theoretically we can either use Hibernate Validation provides Validator, you can use the package Spring thereof. In constructing the spring projects, promote the use of the package through the spring after the method, both methods described here under:

Hibernate Validation:

Foo foo = new Foo();
foo.setAge(22);
foo.setEmail("000");
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<Foo>> set = validator.validate(foo);
for (ConstraintViolation<Foo> constraintViolation : set) {
    System.out.println(constraintViolation.getMessage());
}

Since the dependence of the Hibernate Validation framework, we need to call the factory method to get Hibernate related validator instance to check.

Validation in the relevant sections of spring framework document, you can see the following description:

Spring provides full support for the Bean Validation API. This includes convenient support for bootstrapping a JSR-303/JSR-349 
Bean Validation provider as a Spring bean. This allows for a javax.validation.ValidatorFactory or javax.validation.Validator
to be injected wherever validation is needed in your application. Use the LocalValidatorFactoryBean to configure a default Validator as a Spring bean: bean id=”validator” class=”org.springframework.validation.beanvalidation.LocalValidatorFactoryBean” The basic configuration above will trigger Bean Validation to initialize using its default bootstrap mechanism.
A JSR-303/JSR-349 provider, such as Hibernate Validator, is expected to be present in the classpath and will be detected automatically.

Above paragraph describes the spring of validation full support JSR-303, JSR-349 standard, and encapsulates LocalValidatorFactoryBean as to achieve the validator. It is worth mentioning that the responsibility of this class is actually very important, he's validation system is compatible with a spring and hibernate the validation system, can also be called directly developers, instead of hibernate validator obtained from the factory methods described above. Since we are using springboot, will trigger an automatic configuration of the web module, LocalValidatorFactoryBean has become the default Validator implementations, only need to use auto-injection.

@Autowired
Validator globalValidator; <1>
 
@RequestMapping("/validate")
public String validate() {
    Foo foo = new Foo();
    foo.setAge(22);
    foo.setEmail("000");
 
    Set<ConstraintViolation<Foo>> set = globalValidator.validate(foo);<2>
    for (ConstraintViolation<Foo> constraintViolation : set) {
        System.out.println(constraintViolation.getMessage());
    }
 
    return "success";
}
  • Used Validator interface readers will find that there are two interfaces, one is located javax.validation package, while the other is located at org.springframework.validation package, note that we use here is that the former javax.validation, which is a spring built their own check the interface, LocalValidatorFactoryBean while achieving both of these interfaces.
  • Here check the interface implementation class is the ultimate LocalValidatorFactoryBean.

Based on verification method

@RestController
@Validated <1>
public class BarController {
 
    @RequestMapping("/bar")
    public @NotBlank <2> String bar(@Min(18) Integer age <3>) {
        System.out.println("age : " + age);
        return "";
    }
 
    @ExceptionHandler(ConstraintViolationException.class)
    public Map handleConstraintViolationException(ConstraintViolationException cve){
        Set<ConstraintViolation<?>> cves = cve.getConstraintViolations();<4>
        for (ConstraintViolation<?> constraintViolation : cves) {
            System.out.println(constraintViolation.getMessage());
        }
        Map map = new HashMap();
        map.put("errorCode",500);
        return map;
    }
 
}
  1. Add @Validated notes for the class
  2. And the return value of the parameter calibration method
  3. Add an exception handler can be obtained without checking attribute information related

Based on the method of verification, individuals do not recommend using a combination of feel and the project is not very good.

The idea of ​​using some validation framework

Theoretically spring validation check can achieve a lot of complex, you can even make your Validator get ApplicationContext, acquired all of the spring container resources, such as database validation, into other verification tools, complete portfolio check (such as before and after the password agreement), etc. operations, but to seek a balance between ease of use and the complexity of the package is that we users should be considered as a tool, I respected the way, is the only use its own custom annotations and notes, do some simple, reusable verification. As for the complex verification, the code is included in the business, such as user name, after all, the existence of such a check, not enough to rely solely on database queries, in order to avoid concurrency issues, or have a unique index plus the extra work and the like, is not it?
---------------------
Author: qb170217
Source: CSDN
Original: https: //blog.csdn.net/qb170217/article/details/81232945

Guess you like

Origin www.cnblogs.com/rinack/p/11117023.html