First, the parameter check
In development often need to write some field validation code, such as field non-empty, field length limit, mailbox format verification, and so on, write these and business logic does not relate to the code of the personal feeling there are two problems:
- Verification code tedious, repetitive labor
- The code in the method looks verbose
- Each time you want to see which parameter verification is complete, you need to go through the Verification logic code
Hibernate validator (official documentation) provides a more complete and convenient way to verify your implementation.
spring-boot-starter-web
There is a hibernate-validator
package inside the package that does not need to reference hibernate validator dependencies.
Second, Hibernate validator Check Demo
Let's start with a simple demo and add validator annotations:
Import Org.hibernate.validator.constraints.NotBlank;
Import Javax.validation.constraints.AssertFalse;
Import Javax.validation.constraints.Pattern;
@Getter @setter@noargsconstructorpublic class Demomodel { @NotBlank (message= "User name cannot be empty") private String UserName; @NotBlank (message= "Age cannot be empty") @Pattern (regexp= "^[0-9]{1,2}$", message= "incorrect Age") private String ages; @AssertFalse (message = "must be false") private Boolean isFalse; /** * If it is empty, it is not checked, if not empty, then the checksum * /@Pattern (regexp= "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", message= "Birth date format is incorrect") private String Birthday;}
Post interface validation, Bindingresult is the result set that the validation does not pass:
@RequestMapping ("/demo2") public void Demo2 (@RequestBody @Valid demomodel demo, bindingresult result) { if ( Result.haserrors ()) {for (Objecterror error:result.getAllErrors ()) { System.out.println ( Error.getdefaultmessage ());}}}
The parameters passed in the POST request: {"UserName": "DD", "age": +, "IsFalse": True, "Birthday": "21010-21-12"}
Output Result:
Birth date format is incorrect
Must be false
Age is not correct
Parameter validation is very convenient, and the field annotation + validation does not use the hint information to replace handwritten a large number of non-null and field limit validation code. The following is an in-depth understanding of how to play the parameter check.
This address: http://www.cnblogs.com/mr-yang-localhost/p/7812038.html
Third, Hibernate's check mode
The attentive reader must have found it: The above example returns a collection of all validations that are not passed once, usually in order to verify that the first field does not conform to the validation requirements, you can reject the request directly. Hibernate Validator has the following two types of authentication modes:
1. Normal mode (default is this mode)
Normal mode (all properties are checked and all validation failure messages are returned)
2. Quick Fail return mode
Quick fail return mode (returns if one of the validations fails)
Two modes of authentication Mode configuration: (refer to official documentation)
Failfast:true Quick fail return mode false Normal mode
Validatorfactory validatorfactory = Validation.byprovider (Hibernatevalidator.class)
. Configure ()
. FailFast (True)
. Buildvalidatorfactory ();
Validator Validator = Validatorfactory.getvalidator ();
and (Hibernate.validator.fail_fast:true fast fail return mode false normal mode)
Validatorfactory validatorfactory = Validation.byprovider (Hibernatevalidator.class)
. Configure ()
. AddProperty ("Hibernate.validator.fail_fast", "true")
. Buildvalidatorfactory ();
Validator Validator = Validatorfactory.getvalidator ();
Four, hibernate two kinds of check
Configure Hibernate validator for quick fail return mode:
@Configurationpublic class Validatorconfiguration { @Bean public Validator Validator () { Validatorfactory validatorfactory = Validation.byprovider (hibernatevalidator.class) . Configure () . AddProperty ("Hibernate.validator.fail_fast", "true") . Buildvalidatorfactory (); Validator Validator = Validatorfactory.getvalidator (); return validator;} }
1. Request parameter Check
As shown in the demo sample, the validation request parameters, the @requestbody Demomodel demo between the annotated @Valid, and then add Bindindresult can, multiple parameters, you can add more @valid and Bindingresult, Such as:
public void Test (@RequestBody @Valid Demomodel demo, bindingresult result)
public void Test (@RequestBody @Valid Demomodel demo, bindingresult result, @RequestBody @Valid Demomodel Demo2, Bindingr Esult result2)
@RequestMapping ("/demo2") public void Demo2 (@RequestBody @Valid demomodel demo, bindingresult result) { if ( Result.haserrors ()) {for (Objecterror error:result.getAllErrors ()) { System.out.println ( Error.getdefaultmessage ());}}}
2, get parameter check (@RequestParam parameter check)
Using a checksum bean, there is no way to validate the contents of the Requestparam, typically when processing get requests (or fewer parameters), the following code is used:
@RequestMapping (value = "/demo3", method = requestmethod.get) public void Demo3 (@RequestParam (name = "Grade", Required = true) int grade, @RequestParam (name = "Classroom", required = true) int classroom) { System.out.println (GRA De + "," + classroom); }
Using @valid annotations to annotate requestparam corresponding parameters is not valid and requires the use of @validated annotations to make the validation effective. As shown below:
A. You need to use the methodvalidationpostprocessor bean at this time:
@Bean public methodvalidationpostprocessor methodvalidationpostprocessor () {
/** default is Normal mode and will return all validation not through the information set * /return new Methodvalidationpostprocessor (); }
Or the methodvalidationpostprocessor can be set validator (because validator is not validated at this time, the configuration of validator does not work)
@Bean public methodvalidationpostprocessor methodvalidationpostprocessor () { Methodvalidationpostprocessor postprocessor = new Methodvalidationpostprocessor ();
/** Set Validator mode for fast failure return * /Postprocessor.setvalidator (validator ()); return postprocessor; } @Bean public Validator Validator () { validatorfactory validatorfactory = Validation.byprovider ( Hibernatevalidator.class) . Configure () . AddProperty ("Hibernate.validator.fail_fast", "true") . Buildvalidatorfactory (); Validator Validator = Validatorfactory.getvalidator (); return validator; }
B. Add annotations to the controller on which the method resides @validated
@RequestMapping ("/validation") @RestController @validatedpublic class Validationcontroller { /** If there are only a few objects, The parameters are written directly to the controller layer and then validated at the controller layer. */ @RequestMapping (value = "/demo3", method = requestmethod.get) public void Demo3 (@Range (min = 1, max = 9, Messag E = "Grade only from 1-9") @RequestParam (name = "Grade", required = true) int grade, @Min (value = 1, message = "Class min only 1") @Max (value = full, message = "class Max only") @RequestParam (name = "Classroom", required = true) int classroom) {
system.out.println (Grade + "," + classroom); }}
C. Returning a validation message prompt
You can see that when validation fails, the Constraintviolationexception exception is thrown, using the same catch exception handling:
@ControllerAdvice @componentpublic class Globalexceptionhandler { @ExceptionHandler @ResponseBody @ ResponseStatus (httpstatus.bad_request) public String handle (Validationexception exception) { if (exception instanceof constraintviolationexception) { constraintviolationexception Exs = (constraintviolationexception) Exception; Set<constraintviolation<?>> violations = Exs.getconstraintviolations (); for (constraintviolation<?> item:violations) {
/** print verification does not pass the information * /SYSTEM.OUT.PRINTLN (Item.getmessage ());} } Return "Bad Request,"; }}
D. Validation
Browser Service Request Address: http://localhost:8080/validation/demo3?grade=18&classroom=888
The output information is as follows when the Methodvalidationpostprocessor is not configured for fast failure return:
Grades can only be from 1-9
Class can only be 99
The output information is as follows when the Methodvalidationpostprocessor is configured for fast failure return:
Grades can only be from 1-9
Browser Service Request Address: http://localhost:8080/validation/demo3?grade=0&classroom=0
The output information is as follows when the Methodvalidationpostprocessor is not configured for fast failure return:
Grades can only be from 1-9
Class min only 1
The output information is as follows when the Methodvalidationpostprocessor is configured for fast failure return:
Grades can only be from 1-9
3. Model Check
Model to be verified:
@Datapublic class Demo2 { @Length (min = 5, max = n, message = "length between [5,17]") private String length; /** @Size cannot validate integer, applies to String, Collection, Map and arrays*/ @Size (min = 1, max = 3, message = "Size between [1,3]") Priv Ate String age; @Range (min = $, max = +, message = "Range between [150,250]") private int high; @Size (min = 3,max = 5,message = "List Size in [3,5]") private list<string> List;}
Verify model, all of the following validations pass:
@Autowired private Validator Validator; @RequestMapping ("/demo3") public void Demo3 () { Demo2 Demo2 = new Demo2 (); Demo2.setage ("111"); Demo2.sethigh (); Demo2.setlength ("ABCDE"); Demo2.setlist (New arraylist<string> () {{Add ("111"); Add ("222"); Add ("333");}}); set<constraintviolation<demo2>> Violationset = validator.validate (DEMO2); for (constraintviolation<demo2> model:violationset) { System.out.println (model.getmessage ()); } }
4. Object Cascade Check
An object that contains another object as an attribute, plus @valid on the property, can validate validation within an object that is an attribute: (Validating the Demo2 field when validating the Demo2 example)
@Datapublic class Demo2 { @Size (min = 3,max = 5,message = "List Size in [3,5]") private list<string> list;
@NotNull @Valid private Demo3 Demo3;} @Datapublic class Demo3 { @Length (min = 5, max = n, message = "length between [5,17]") private String Extfield;}
Cascading checks:
/** the bean*/ @Autowired private Validator Validator is configured with a quick fail return; @RequestMapping ("/demo3") public void Demo3 () { Demo2 Demo2 = new Demo2 (); Demo2.setlist (New arraylist<string> () {{Add ("111"); Add ("222"); Add ("333");}}); Demo3 Demo3 = new Demo3 (); Demo3.setextfield ("a"); Demo2.setdemo3 (DEMO3); set<constraintviolation<demo2>> Violationset = validator.validate (DEMO2); for (constraintviolation<demo2> model:violationset) { System.out.println (model.getmessage ()); } }
You can verify the Extfield field of the Demo3.
5. Group Check
Conclusion: When the packet sequence is checked, the verification is done according to the specified grouping order, the previous verification does not pass, and the subsequent grouping does not validate.
There is a scenario where user information is added without validating the UserID (because of system generation), and when modifying the UserID, the user-to-validator group verification function is required.
Set validator to the normal authentication mode ("Hibernate.validator.fail_fast", "false"), using the validation groupa, GROUPB, and model:
Groupa, GroupB:
Public interface Groupa {}public interface GroupB {}
Verify Model:person
View Code
As shown in the person above, the 3 groups verify the fields as follows:
- Groupa validation field UserID;
- GROUPB validation field username, sex;
- Default Validation field, age (defaults are the validator of the group)
A, group
To verify only the grouping of Groupa, GROUPB tags:
@RequestMapping ("/demo5")
public void Demo5 () {
Person p = new person ();
/**groupa verification does not pass */
P.setuserid (-12);
/**groupa Verification via */
P.setuserid (12);
P.setusername ("a");
P.setage (110);
P.setsex (5);
Set<constraintviolation<person>> validate = Validator.validate (P, Groupa.class, Groupb.class);
for (constraintviolation<person> item:validate) {
SYSTEM.OUT.PRINTLN (item);
}
}
Or
@RequestMapping ("/demo6") public void Demo6 (@Validated ({groupa.class, groupb.class}) person P, Bindingresult Result) { if (result.haserrors ()) { list<objecterror> allerrors = result.getallerrors (); for (Objecterror error:allerrors) { System.out.println (error);}} }
Groupa, GroupB, and default all verify that the case is not passed:
The verification information is as follows:
Constraintviolationimpl{interpolatedmessage= ' must be in [4,20] ', Propertypath=username, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' must be in [4,20] '}
Constraintviolationimpl{interpolatedmessage= ' must be greater than 0 ', Propertypath=userid, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' must be greater than 0 '}
Constraintviolationimpl{interpolatedmessage= ' sex must be in [0,2] ', Propertypath=sex, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' sex must be in [0,2] '}
Groupa validation Pass, GroupB, default validation does not pass the case:
The verification information is as follows:
Constraintviolationimpl{interpolatedmessage= ' must be in [4,20] ', Propertypath=username, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' must be in [4,20] '}
Constraintviolationimpl{interpolatedmessage= ' sex must be in [0,2] ', Propertypath=sex, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' sex must be in [0,2] '}
B, Group sequence
In addition to specifying whether or not to validate by group, you can also specify the validation order for the group, the previous group validation does not pass, and subsequent groups do not validate:
Specify the sequence of the group (groupa "GroupB" Default):
@GroupSequence ({groupa.class, Groupb.class, default.class}) public interface GroupOrder {}
Test Demo:
@RequestMapping ("/demo7") public void Demo7 () {person p = new Person (); /**groupa verification does not pass through * ///p.setuserid ( -12); /**groupa Verification via * /P.setuserid (n); P.setusername ("a"); P.setage (a); P.setsex (5); Set<constraintviolation<person>> validate = Validator.validate (P, grouporder.class); for (constraintviolation<person> item:validate) { System.out.println (item); } }
Or
@RequestMapping ("/demo8") public void Demo8 (@Validated ({grouporder.class}) person P, bindingresult result) { if (result.haserrors ()) { list<objecterror> allerrors = result.getallerrors (); for (Objecterror error:allerrors) { System.out.println (error);}} }
Groupa, GroupB, and default all verify that the case is not passed:
The verification information is as follows:
Constraintviolationimpl{interpolatedmessage= ' must be greater than 0 ', Propertypath=userid, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' must be greater than 0 '}
Groupa validation Pass, GroupB, default validation does not pass the case:
The verification information is as follows:
Constraintviolationimpl{interpolatedmessage= ' must be in [4,20] ', Propertypath=username, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' must be in [4,20] '}
Constraintviolationimpl{interpolatedmessage= ' sex must be in [0,2] ', Propertypath=sex, Rootbeanclass=class Validator.demo.project.model.Person, messagetemplate= ' sex must be in [0,2] '}
Conclusion: When the packet sequence is checked, the verification is done according to the specified grouping order, the previous verification does not pass, and the subsequent grouping does not validate.
Five, Custom Validator
As a general rule, custom validation can solve many problems. But there is also a time when the situation is not satisfied, at this time, we can implement validator interface, customize the authenticator we need.
A custom case validator is implemented as follows:
public enum Casemode { UPPER, LOWER;} @Target ({elementtype.method, Elementtype.field, Elementtype.annotation_type}) @Retention (Retentionpolicy.runtime) @Constraint (Validatedby = checkcasevalidator.class) @Documentedpublic @interface checkcase { String message () Default ""; Class<?>[] Groups () default {}; class<? Extends payload>[] Payload () default {}; Casemode value ();} public class Checkcasevalidator implements Constraintvalidator<checkcase, string> { private Casemode Casemode; public void Initialize (Checkcase checkcase) { This.casemode = Checkcase.value (); } public Boolean isValid (String s, Constraintvalidatorcontext constraintvalidatorcontext) { if (s = = null) { return true; } if (Casemode = = casemode.upper) { return s.equals (S.touppercase ()); } else { return s.equals ( S.tolowercase ());}}}
Model to validate:
public class demo{ @CheckCase (value = Casemode.lower,message = "UserName must be lowercase") private String userName; Public String GetUserName () { return userName; } public void Setusername (String userName) { this.username = userName; } }
Validator configuration:
@Bean public Validator Validator () { validatorfactory validatorfactory = Validation.byprovider ( Hibernatevalidator.class) . Configure () . AddProperty ("Hibernate.validator.fail_fast", "true") . Buildvalidatorfactory (); Validator Validator = Validatorfactory.getvalidator (); return validator; }
Validation test:
@RequestMapping ("/demo4") public void Demo4 () { Demo demo = new Demo (); Demo.setusername ("UserName"); Set<constraintviolation<demo>> validate = Validator.validate (Demo); for (constraintviolation<demo> dem:validate) { System.out.println (dem.getmessage ()); } }
Output Result:
Username must be lowercase
Vi. Common annotations
Built-in constraint in Bean Validation
@Null The annotated element must be Null
@NotNull annotated element must not be null
@AssertTrue The annotated element must be true
@AssertFalse The annotated element must be false
@Min (value) The annotated element must be a number whose value must be greater than or equal to the specified minimum value
@Max (value) The annotated element must be a number whose value must be less than or equal to the specified maximum value
@DecimalMin (value) The annotated element must be a number whose value must be greater than or equal to the specified minimum value
@DecimalMax (value) The annotated element must be a number whose value must be less than or equal to the specified maximum value
@Size (max=, min=) the size of the annotated element must be within the specified range
@Digits (integer, fraction) The annotated element must be a number whose value must be within an acceptable range
@Past The annotated element must be a past date
@Future The annotated element must be a future date
@Pattern (regex=,flag=) The annotated element must conform to the specified regular expression
Hibernate Validator Additional constraint
@NotBlank (message =) validation string is not null and must be longer than 0
@Email The annotated element must be an e-mail address
@Length (min=,max=) The size of the annotated string must be within the specified range
@NotEmpty The annotated string must be non-empty
@Range (min=,max=,message=) The annotated element must be within the appropriate range
Greater than 0.01, not including 0.01
@NotNull
@DecimalMin (value = "0.01", inclusive = False)
Private Integer GreaterThan;
Greater than or equal to 0.01
@NotNull
@DecimalMin (value = "0.01", inclusive = True)
Private BigDecimal Greatorequalthan;
@Length (min = 1, max =, message = "message cannot be empty")
You cannot use length incorrectly as a range
@Range (min = 1, max =, message = "message cannot be empty")
Private String message;
Vii. references
Resources:
- http://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html_single/#validator-gettingstarted
Springboot using Hibernate validator Checksum