Together to learn SpringBoot (xvii) Elegant parameter check

Check parameters
in development often need to write some field validation code, such as non-empty field, field length limit, the mailbox format validation and so on, to write these little relationship with the business logic code that personal feeling there are two problems:

Validation code cumbersome duplication of effort
within method code more verbose
each depends on which parameter validation is complete, you need to read the code validation logic
you see this? I think not - what's a good way to not

String test1 public (String name) {
IF (name == null) {
the throw a NullPointerException new new ( "name can not be blank");
}
IF (name.length () <2 || name.length ()> 10) {
the throw new RuntimeException ( "name length must be 2 - between 10");
}
return "Success";
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
using the hibernate-validator
Spring-Boot-Web Starter-package package inside hibernate-validator , you do not need to rely on references to hibernate validator. Add the spring-boot-starter-web can depend in pom.xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
1
2
3
4
创建如下实体

@Data
public class Book {
Private ID Integer;
@NotBlank (Message = "name NOT NULL")
@Length 2, max = 10, Message = "name length must (min = In {min} - {max} between ")
Private String name;
}
. 1
2
. 3
. 4
. 5
. 6
. 7
physical check
and write it in the controller to verify

Verification of parameters plus @RequestBody

@RequestMapping ( "/ Test")
public String Test (@Validated @RequestBody Book Book) {
return "Success";
}
. 1
2
. 3
. 4
at this time it will be done MethodArgumentNotValidException abnormal global exception handler in ControllerAdvice

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(Exception.class)
public ResponseEntity<ModelMap> ex(Exception e) {
log.error("请求参数不合法。", e);
ModelMap modelMap = new ModelMap();
if (e instanceof MethodArgumentNotValidException) {
modelMap.put("message", getErrors(((MethodArgumentNotValidException) e).getBindingResult()));
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(modelMap);
}

private Map<String, String> getErrors(BindingResult result) {
Map<String, String> map = new HashMap<>();
List<FieldError> list = result.getFieldErrors();
for (FieldError error : list) {
map.put(error.getField(), error.getDefaultMessage());
}
return map;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
如果不加呢?

@RequestMapping ( "/ Test")
public String Test (@Validated Book Book) {
return "Success";
}
. 1
2
. 3
. 4
will BindException an abnormality is determined and can be added in ControllerAdvice

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(Exception.class)
public ResponseEntity<ModelMap> ex(Exception e) {
log.error("请求参数不合法。", e);
ModelMap modelMap = new ModelMap();
if (e instanceof BindException) {
modelMap.put("message", getErrors(((BindException) e).getBindingResult()));
} else if (e instanceof MethodArgumentNotValidException) {
modelMap.put("message", getErrors(((MethodArgumentNotValidException) e).getBindingResult()));
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(modelMap);
}
private Map<String, String> getErrors(BindingResult result) {
Map<String, String> map = new HashMap<>();
List <FieldError> = result.getFieldErrors List ();
for (FieldError error: List) {
map.put (error.getField (), error.getDefaultMessage ());
}
return Map;
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
20 is
the authentication parameter
if get request parameters?

@RequestMapping ( "/ Test")
public String Test (@Validated @NotBlank (Message = "name NOT NULL") String name) {
System.out.println ( "111");
return "Success";
}
. 1
2
3
4
5
we found this at all so bad, in fact, does this need to join in on a class

@Validated
@RestController
public class TestController {
@RequestMapping ( "/ Test")
public String Test (@NotBlank (Message = "name NOT NULL") String name) {
System.out.println ( "111");
return " Success ";
}
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
so that it can take effect, at this time it may be so treated returned ConstraintViolationException exception in the global exception

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(Exception.class)
public ResponseEntity<ModelMap> ex(Exception e) {
log.error("请求参数不合法。", e);
ModelMap modelMap = new ModelMap();
if (e instanceof HttpMediaTypeException) {
modelMap.put("message", "请求体不对");
} else if (e instanceof ConstraintViolationException) {
ConstraintViolationException exs = (ConstraintViolationException) e;
Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
modelMap.put("message", getErrors(violations));
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(modelMap);
}
private Map<String, String> getErrors(Set<ConstraintViolation<?>> violations) {
Map <String, String> = new new Map the HashMap <> ();
for (ConstraintViolation Item <?>: Violations) {
map.put (item.getPropertyPath () toString (), item.getMessage ().);
}
Return Map ;
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
20 is
21 is
the Model verification
as not want to verify the parameter passing through it? Just want to verify that an entity how to play with?

This can be solved

@RestController
public class TestController {
@Autowired
private Validator validator;
@RequestMapping("/test")
public Map<String, String> test() {
Book book = new Book();
book.setId(1).setName("");
Set<ConstraintViolation<Book>> violationSet = validator.validate(book);
return getErrors(violationSet);
}
private <T> Map<String, String> getErrors(Set<ConstraintViolation<T>> violations) {
Map<String, String> map = new HashMap<>();
for (ConstraintViolation<?> item : violations) {
map.put(item.getPropertyPath().toString(), item.getMessage());
}
return map;
}
}
1
2
3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
objects cascaded parity
added in the book, such that an entity has changed at this time the object how to do?

Only you need to physically join @Valid

@Data
@Accessors(chain = true)
public class Book {

Integer ID Private;
@NotBlank (Message = "name NOT NULL")
@Length (= 2 min, max = 10, Message = "name length must be {min} - max} between {")
Private String name;
@Valid
Private author author;

@Data
@Accessors (= catena alberghiera to true)
public static class the Author {
@NotBlank (Message = "author.name NOT NULL")
Private String name;
}
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
17
18
Hibernate Validator-check mode
at this time to say something to the

When the example above returns a collection of all the disposable authentication fails, usually in order to verify the first field does not meet the validation requirements, you can directly reject the request. Hibernate Validator There are two verification modes

Normal mode (the default mode)
Normal mode (check will complete all of the properties, and then return all validation failure information)

Failure Quick return mode
quickly returns a failure mode (as long as there is a verification fails, the return)

true fail-fast mode returns false normal mode

ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
1
2
3
4
5
或者这样配

ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
1
2
3
4
5
这样配置就行了

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor () {
MethodValidationPostProcessor PostProcessor new new MethodValidationPostProcessor = ();
/ ** set the validator failure mode for quick return * /
postProcessor.setValidator (validator ());
return PostProcessor;
}

@Bean
public Validator the Validator () {
ValidatorFactory validatorFactory = Validation.byProvider (HibernateValidator.class)
.configure ()
.addProperty ( "hibernate.validator.fail_fast", "to true")
.buildValidatorFactory ();
the Validator Validator validatorFactory.getValidator = ( );
return Validator;
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
packet checksum
packet sequence verification, the specified sequence of packets to verify that the foregoing authentication fails, the packet back it is not verified.

Such a scenario, the new time, does not require authentication Id (because the system is generating); modify the time required to verify Id, which can be used when the user authentication packet to the validator.

Validator arranged to validate normal mode ( "hibernate.validator.fail_fast", "false"), used to verify GroupA, GroupB and entities:

GroupA, GroupB
. 1
public interface GroupA {
}
public interface GroupB {
}
. 1
2
. 3
. 4
and to transform what entity Book

@Data
@Accessors (= catena alberghiera to true)
public class Book {
@NotBlank
@range (= min. 1, max = Integer.MAX_VALUE, Message = "must be greater than 0", Groups GroupA.class = {})
Private ID Integer;
@NotBlank (message = "name NOT nULL")
@Length (= min. 4, max = 20 is, Message = "name length must be {min} - max} between {", Groups GroupB.class = {})
Private String name;
@NotBlank
@range (min = 0, max = 100, Message = "must be aged in [0,100]", Groups DEFAULT.class = {})
Private Integer Age;
}

. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
GroupA ID verification field;
GroupB verification field name;
the Default verification field age (Default Validator own default packet)
so to verify

@RequestMapping("/test")
public void test() {
Book book = new Book();
/**GroupA验证不通过*/
book.setId(-10086);
/**GroupA验证通过*/
//book.setId(10010);
book.setName("a");
book.setAge(110);
Set<ConstraintViolation<Book>> validate = validator.validate(book, GroupA.class, GroupB.class);
for (ConstraintViolation<Book> item : validate) {
System.out.println(item);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
或者这样

@RequestMapping ( "/ Test")
public void Test (@Validated ({GroupA.class, GroupB.class}) Book Book, the BindingResult Result) {
IF (result.hasErrors ()) {
List <ObjectError> AllErrors = result.getAllErrors ();
for (ObjectError error: AllErrors) {
System.out.println (error);
}
}
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
of course such a group to give MUST verify the sequence, or not or can not be achieved

@GroupSequence ({GroupA.class, GroupB.class, DEFAULT.class})
public interface GroupOrder {
}
. 1
2
. 3
so that like to play and so

SET <ConstraintViolation <>> Book the validate = Validator.validate (Book, GroupOrder.class);
. 1
@Validated (GroupOrder.class} {) Book Book, the BindingResult Result
. 1
Note item
if you do not want to see the global knockdown abnormal errors can intuitively BindingResult result added in the process parameters

So you can play a single

Test void public () (@ DemoModel the Validated Demo, the BindingResult Result)
. 1
Verify plurality of words can play this

void the Test public () (Demo @ the Validated DemoModel, BindingResult the Result, the Validated @ DemoModel demo2, BindingResult result2)
1
custom validator
general, custom validation can solve many problems. But there are times unable to meet the conditions, then we can achieve validator interface, custom validator they need.

First, it defined an annotation, add comments @Constraint binding validation class in the annotation

@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = DateTimeValidator.class)
public @interface DateTime {

String message () default "format error";

String format() default "yyyy-MM-dd";

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

Class <? The extends Payload> [] payload () {} default;
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
then verified to see class implements ConstraintValidator <A extends Annotation, T> can be denoted by A is a comment T parameters

public class DateTimeValidator implements ConstraintValidator<DateTime, String> {

private DateTime dateTime;

@Override
public void initialize(DateTime dateTime) {
this.dateTime = dateTime;
}

@Override
public Boolean isValid (String value, context ConstraintValidatorContext) {
// If the value is not null format verification, verification can be used to empty the like @NotBlank @NotNull @NotEmpty annotation to control separation of duties
if (value == null ) {
return to true;
}
String = DateTime.Format the format ();
IF (value.length ()! = format.length ()) {
return to false;
}
the SimpleDateFormat SimpleDateFormat the SimpleDateFormat new new = (the format);
the try {
simpleDateFormat.parse ( value);
} the catch (a ParseException E) {
return to false;
}
return to true;
}
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
. 9
10
. 11
12 is
13 is
14
15
16
. 17
18 is
. 19
20 is
21 is
22 is
23 is
24
25
26 is
27
28
and so on the line with

@Validated
@RestController
public class ValidateController {
@GetMapping ( "/ Test")
public String Test (@datetime (Message = "format you entered is incorrect, the correct format: {format}", format = "yyyy-MM-dd HH: mm ") String DATE) {
return" Success ";
}
}
. 1
2
. 3
. 4
. 5
. 6
. 7
. 8
JSR-303 introduction Note
hibernate-validator JSR-303 were achieved just to mention notes under javax.validation package, Similarly there hibernate-validator validation package in spring-boot-starter-web package, which contains a number javax.validation no comments, are interested can look at

Explanatory notes
@NotNull limit must not be null
authentication @NotEmpty annotation element value is not null and is not empty (the string length is not 0, set size is not 0)
@NotBlank verified annotation element value is not null (not null, the space after removal of the first length of 0), unlike @ NotEmpty, @ NotBlank applied only in the comparison string and removes blank string
@Pattern (value) must meet the restrictions specified regular expression
@Size (max , min) limiting character length must be between the max min (can also be used in the set)
@email annotation element value is verified email, regular expressions may be specified as an email address and a flag by a custom
@Max (value) must be no greater than a limit value specified numeric
limits @Min (value) must be less than a specified value of a numeric
limit must be no greater than a specified value @DecimalMax (value) of the digital
@DecimalMin (value) must be restricted to an value less than the specified number
@Null restricted only null (the rarely used)
@AssertFalse limit must be false (rarely used)
@AssertTrue limit must be true (rarely used)
@Past limit Must be a date in the past
@Future limit must be a future date
@Digits (integer, fraction) limit must be a decimal digit of the integer part and can not exceed the integer, the fractional part digits can not exceed fraction (rarely used )
Oh right, these check not only can be used anywhere in the controller layer

Guess you like

Origin www.cnblogs.com/hyhy904/p/10962034.html