In daily interface development, in order to prevent illegal parameters from affecting the business, it is often necessary to verify the parameters of the interface. For example, when logging in, it is necessary to verify whether the user name and password are empty. When adding a user, verify whether the format of the user's email address and mobile phone number is correct. It would be too cumbersome to rely on code to verify interface parameters one by one, and the readability of the code would be extremely poor.
Validator
The framework is designed to solve the problem of developers writing less code during development and improving development efficiency; Validator is specially used to verify interface parameters, such as common required verification, email Format verification, the username must be between 6 and 12, etc.
Next let’s take a look at how to integrate the parameter verification framework in SpringbBoot.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
package com.didiplus.modules.sys.domain; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; /** * Author: didiplus * Email: 972479352@qq.com * CreateTime: 2022/4/25 * Desc: 字典类型领域模型 */ @Data @ApiModel(value = "字典类型") public class SysDictType { @ApiModelProperty("ID") private String id; @NotBlank(message = "字典名称必填项") @ApiModelProperty(value = "字典名称",example = "用户ID") private String typeName; @NotBlank(message = "字典编码不能为空") @ApiModelProperty(value = "字典编码") private String typeCode; @Email(message = "请填写正确的邮箱地址") @ApiModelProperty(value = "字典编码") private String email; @ApiModelProperty(value = "字典描述") private String description; @NotBlank(message = "字典状态不能为空") @ApiModelProperty(value = "字典状态") private String enable; }
Common constraint annotations are as follows:
Annotation | Function |
---|---|
@AssertFalse | can be null, if not null Must be false |
@AssertTrue | can be null, if not null it must be true |
@DecimalMax | The setting cannot exceed the maximum value |
@DecimalMin | The setting cannot exceed the minimum value |
@Digits | The setting must be a number and the number of integer digits and decimal digits must be within the specified range |
@Future | The date must be within the current date The future |
@Past | date must be in the past |
@Max | max of the current date This maximum value must not be exceeded |
@Min | The maximum must not be less than this minimum value |
@NotNull | Cannot be null, can be empty |
@Null | Must be null |
@Pattern | Must satisfy the specified regular expression |
@Size | The size() value of collections, arrays, maps, etc. must be within the specified range |
Must be in email format | |
@Length | The length must be within the specified range |
@NotBlank | The string cannot be null, and the string after trim() cannot be equal to "" |
@NotEmpty | Cannot be null, size() of collections, arrays, maps, etc. cannot be 0; string trim() can be equal to "" |
@Range | The value must be Within the specified range |
@URL | must be a URL |
package com.didiplus.modules.sys.controller; import com.didiplus.modules.sys.domain.SysDictType; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.parameters.RequestBody; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Author: didiplus * Email: 972479352@qq.com * CreateTime: 2022/4/25 * Desc: 数据字典控制器 */ @RestController @Api(tags = "数据字典") @RequestMapping("/api/sys/dictType") public class SysDictTypeController { @ApiOperation("字典添加") @PostMapping("/add") public SysDictType add(@Validated @RequestBody SysDictType sysDictType) { return sysDictType; } @ApiOperation("字典修改") @PutMapping("/edit") public SysDictType edit(@Validated @RequestBody SysDictType sysDictType) { return sysDictType; } }
这里我们先定义两个方法add
,edit
,都是使用了 @RequestBody
注解,用于接受前端发送的json
数据。
通过接口文档看到前三个字段都是必填项。
由于email的格式不对就被拦截了,提示是因为邮箱地址不对。
虽然我们之前定义了全局异常拦截器,也看到了拦截器确实生效了,但是Validator
校验框架返回的错误提示太臃肿了,不便于阅读,为了方便前端提示,我们需要将其简化一下。
直接修改之前定义的 RestExceptionHandler
,单独拦截参数校验的三个异常:
javax.validation.ConstraintViolationException
org.springframework.validation.BindException
org.springframework.web.bind.MethodArgumentNotValidException
代码如下:
package com.didiplus.common.web.response.Handler; import com.didiplus.common.web.response.Result; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; import java.util.stream.Collectors; /** * Author: didiplus * Email: 972479352@qq.com * CreateTime: 2022/4/24 * Desc: 默认全局异常处理。 */ @RestControllerAdvice public class RestExceptionHandler { /** * 默认全局异常处理。 * @param e the e * @return ResultData */ @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class}) public ResponseEntity<Result<String>> handleValidatedException(Exception e) { Result<String> result = null; if (e instanceof MethodArgumentNotValidException) { MethodArgumentNotValidException ex =(MethodArgumentNotValidException) e; result = Result.failure(HttpStatus.BAD_REQUEST.value(), ex.getBindingResult().getAllErrors().stream() .map(ObjectError::getDefaultMessage) .collect(Collectors.joining(";")) ); } else if (e instanceof ConstraintViolationException){ ConstraintViolationException ex = (ConstraintViolationException) e; result = Result.failure(HttpStatus.BAD_REQUEST.value(), ex.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .collect(Collectors.joining(";")) ); }else if (e instanceof BindException) { BindException ex = (BindException ) e; result = Result.failure(HttpStatus.BAD_REQUEST.value(), ex.getAllErrors().stream() .map(ObjectError::getDefaultMessage) .collect(Collectors.joining(";")) ); } return new ResponseEntity<>(result,HttpStatus.BAD_REQUEST); } }
美化之后错误信息提示更加友好
虽然Spring Validation 提供的注解基本上够用,但是面对复杂的定义,我们还是需要自己定义相关注解来实现自动校验。
比如上面实体类中添加的sex性别属性,只允许前端传递传 M,F 这2个枚举值,如何实现呢?
package com.didiplus.common.annotation; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * Author: didiplus * Email: 972479352@qq.com * CreateTime: 2022/4/26 * Desc: */ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Repeatable(EnumString.List.class) @Documented @Constraint(validatedBy = EnumStringValidator.class)//标明由哪个类执行校验逻辑 public @interface EnumString { String message() default "value not in enum values."; Class<?>[] groups() default {}; Class<? extends Payload>[] palyload() default {}; /** * @return date must in this value array */ String[] value(); /** * Defines several {@link EnumString} annotations on the same element. * * @see EnumString */ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Documented @interface List { EnumString[] value(); } }
package com.didiplus.common.annotation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.Arrays; import java.util.List; /** * Author: didiplus * Email: 972479352@qq.com * CreateTime: 2022/4/26 * Desc: */ public class EnumStringValidator implements ConstraintValidator<EnumString,String> { private List<String> enumStringList; @Override public void initialize(EnumString constraintAnnotation) { enumStringList = Arrays.asList(constraintAnnotation.value()); } @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { if(value == null) { return true; } return enumStringList.contains(value); } }
@ApiModelProperty(value = "性别") @EnumString(value = {"F","M"}, message="性别只允许为F或M") private String sex;
一个对象在新增的时候某些字段是必填,在更新是有非必填。如上面的 SysDictType
中id
属性在新增操作时都是必填。 面对这种场景你会怎么处理呢?
其实 Validator
校验框架已经考虑到了这种场景并且提供了解决方案,就是分组校验。 要使用分组校验,只需要三个步骤:
package com.didiplus.common.base; import javax.validation.groups.Default; /** * Author: didiplus * Email: 972479352@qq.com * CreateTime: 2022/4/26 * Desc: */ public interface ValidGroup extends Default { interface Crud extends ValidGroup{ interface Create extends Crud{ } interface Update extends Crud{ } interface Query extends Crud{ } interface Delete extends Crud{ } } }
@Null(groups = ValidGroup.Crud.Create.class) @NotNull(groups = ValidGroup.Crud.Update.class,message = "字典ID不能为空") @ApiModelProperty("ID") private String id;
The above is the detailed content of How does SpringBoot perform parameter verification?. For more information, please follow other related articles on the PHP Chinese website!