


1. 安装依赖
在pom.xml
中添加
<!--验证器(validation) 开始部分 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--验证器(validation) 结束部分 -->
在 SpringBoot 2.3.x 以前 SpringBoot 包 默认引入 spring-boot-starter-validation 包,而自 SpringBoot 2.3.x 以后官方将其排除,需要单独引入。
2. 验证流程图

3. Hibernate的校验模式
Hibernate Validator有普通模式(默认是这个模式) 和 快速模式两种验证模式。
普通模式: 会校验完所有的属性,然后返回所有的验证失败信息。 快速模式: 只要有一个验证失败,则返回。
4. 添加Hibernate配置
配置hibernate Validator为快速模式
package com.hui.javalearn.config;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@Configuration
public class ValidatorConfig {
/**
* 配置验证器
*
* @return Validator
*/
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
// 设置为快速返回模式
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
5. 验证Get参数
5.1 配置方法验证器
Hibernate Validator是可以在方法级验证参数的,需要我们在Validator的配置中,添加MethodValidationPostProcessor
Bean,完整配置如下:
package com.hui.javalearn.config;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@Configuration
public class ValidatorConfig {
/**
* 设置方法参数验证器
*/
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
// 设置validator模式为快速失败返回
postProcessor.setValidator(validator());
return postProcessor;
}
/**
* 配置验证器
* @return Validator
*/
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
// 设置为快速返回模式
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return validatorFactory.getValidator();
}
}
5.2 Get参数验证
GET
请求参数中一般是没有实体对象的,所以不能使用 @Valid
,而是通过需要使用@Validated
注解来使得验证生效。
package com.hui.javalearn.controller;
import com.hui.javalearn.common.ApiBaseController;
import com.hui.javalearn.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
/**
* @author liuqh
*/
@RestController
@Api(tags = "用户管理")
@RequestMapping("/user")
@Validated
public class UserController extends ApiBaseController {
@Autowired
private UserService userService;
@ApiOperation("登录")
@GetMapping("/login")
public String login(
@Pattern(regexp = "1[3|4|5|7|8][0-9]\\d{8}",message = "手机号格式不正确!")
@RequestParam("phone") String phone,
@NotEmpty(message = "密码不能为空") @RequestParam("pwd") String pwd
) {
return "phone:" + phone + " pwd: " + pwd;
}
}
@注意: 在要使用参数验证的类上一定要加上
@Validated
注解,否则无效
访问: http://127.0.0.1:8080/user/login
2020-09-14 15:46:19.770 WARN 15005 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'phone' is not present]
5.3 拦截错误
如果验证不通过,会抛出MissingServletRequestParameterException
异常,我们可以在全局的异常处理器里面处理验证错误。
新增全局的异常处理器: GlobalExceptionHandler.java
,代码如下:
package com.hui.javalearn.exception;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Set;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 捕获参数异常处理
* @param e
* @param model
* @return String
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public String handelMissingServletRequestParameterException(MissingServletRequestParameterException e, Model model)
{
log.error("缺少请求参数", e);
String message = "缺少请求参数: " + e.getMessage();
// 临时使用map,方便测试。
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("msg",message);
stringObjectHashMap.put("code",400);
return JSON.toJSONString(stringObjectHashMap);
}
/**
* spring validator 方法参数验证异常拦截
*
* @param e 绑定验证异常
* @return 错误返回消息
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public String defaultErrorHandler(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String message = violation.getMessage();
log.info("数据验证异常:{}", violation.getMessage());
// 临时使用map,方便测试。
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("msg",message);
stringObjectHashMap.put("code",400);
return JSON.toJSONString(stringObjectHashMap);
}
}
访问: http://127.0.0.1:8080/user/login
{
"msg": "Required String parameter 'phone' is not present",
"code": 400
}
6.验证POST参数(实体类)
接口上的Bean验证,需要在参数前加上@Valid
或Spring的 @Validated
注解,这两种注释都会导致应用标准Bean验证。如果验证不通过会抛出BindException
异常,并变成400(BAD_REQUEST)响应。另外如果参数前有@RequestBody
注解,验证错误会抛出MethodArgumentNotValidException
异常。
6.1 定义实体类(dto)
package com.hui.javalearn.dto.param;
import com.hui.javalearn.utils.validate.Phone;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class UserParamDTO {
@ApiModelProperty(value = "用户名")
@NotBlank(message = "用户名不能为空")
private String nickName;
@Phone
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "身份证号码")
@NotBlank(message = "身份证号码不能为空")
private String idCard;
}
6.2 编写Controller
package com.hui.javalearn.controller;
import com.hui.javalearn.common.ApiBaseController;
import com.hui.javalearn.common.ResponseResult;
import com.hui.javalearn.dto.param.UserParamDTO;
import com.hui.javalearn.model.UserModel;
import com.hui.javalearn.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import java.util.HashMap;
import java.util.List;
/**
* @author liuqh
*/
@RestController
@Api(tags = "用户管理")
@RequestMapping("/user")
@Validated
public class UserController extends ApiBaseController {
@Autowired
private UserService userService;
@ApiOperation("注册")
@PostMapping("/register")
public ResponseResult<HashMap<String, Integer>> register(@Valid UserParamDTO userParamDTO){
int i = userService.insertUser(userParamDTO);
HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
stringIntegerHashMap.put("id",i);
return ResponseResult.success(stringIntegerHashMap);
}
}
请求结果:
curl -X POST "http://127.0.0.1:8080/user/register" -H "Request-Origion:SwaggerBootstrapUi" -H "accept:*/*" -H "Content-Type:application/json" -d "nickName=张三" -d "phone=111" -d "pwd=11"
# 报错:
2020-09-14 18:15:02.595 WARN 17753 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
6.3 拦截错误
修改全局的异常处理器GlobalExceptionHandler.java
,修改的代码如下:
package com.hui.javalearn.exception;
import com.hui.javalearn.common.ResponseResult;
import com.hui.javalearn.common.enums.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 捕获参数异常处理
*
* @param e
* @param model
* @return String
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseResult<?> handelMissingServletRequestParameterException(MissingServletRequestParameterException e, Model model) {
log.error("缺少请求参数", e);
String message = "缺少请求参数: " + e.getMessage();
return ResponseResult.error(ResultEnum.ERROR_PARAM.getCode(), message);
}
/**
* spring validator 方法参数验证异常拦截
*
* @param e 绑定验证异常
* @return 错误返回消息
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public ResponseResult<?> defaultErrorHandler(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String message = violation.getMessage();
log.info("数据验证异常:{}", violation.getMessage());
return ResponseResult.error(ResultEnum.ERROR_PARAM.getCode(), message);
}
/**
* 拦截实体类参数(参数前没有@RequestBody注解)校验失败的错误
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public ResponseResult<?> bindExceptionHandler(BindException e) {
ObjectError objectError = e.getAllErrors().get(0);
log.info("数据验证异常:{}", objectError.getDefaultMessage());
return ResponseResult.
error(ResultEnum.ERROR_PARAM.getCode(), objectError.getDefaultMessage());
}
/**
* 拦截实体类参数(参数前有@RequestBody注解)校验失败的错误
*
* @param e
* @return
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseResult<?> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
return ResponseResult.
error(ResultEnum.ERROR_PARAM.getCode(),objectError.getDefaultMessage() );
}
}
ResponseResult
是我自定义的公共类,你也可以像Get方式一样使用map做使用测试
7. 验证相关的注解
hibernate-validator沿用了validation-api中的所有注解约束,同时也定义了一些自己的约束:
| constraint | 描述 | 来源 |
|---|---|---|
| @AssertFalse | 被约束的元素必须是false | validation-api |
| @AssertTrue | 被约束的元素必须是true | validation-api |
| @DecimalMax | 被约束的元素必须是数字且其必须小于等于指定值 | validation-api |
| @DecimalMin | 被约束的元素是数字且其必须大于等于指定值 | validation-api |
| @Digits | 被约束的元素必须是数字且其在约束范围内 | validation-api |
| @Future | 被约束的元素是未来的时间 | validation-api |
| @Max | 被约束的元素是数字且其必须小于等于指定值 | validation-api |
| @Min | 被约束的元素是数字且其必须大于等于指定值 | validation-api |
| @NotNull | 被约束的原属不能为null | validation-api |
| @Null | 被约束的原始必须为null | validation-api |
| @Past | 被约束的元素必须是过去的时间 | validation-api |
| @Pattern | 被约束的元素必须符合自定正则表达式 | validation-api |
| @Size | 被约束的元素必须在范围内 | validation-api |
| @URL | 被约束的元素必须是一个URL | hibernate-validator |
| @ScriptAssert | 验证类级别脚本 | hibernate-validator |
| @SafeHtml | 被约束的元素必须是一个HTML | hibernate-validator |
| @Range | 被约束的元素必须是在[min,max]范围 | hibernate-validator |
| @ParameterScriptAssert | 验证参数级别脚本 | hibernate-validator |
| @NotEmpty | 验证字符串、集合、字典或数组是否为null或者空 | hibernate-validator |
| @NotBlank | 验证字符串是否为null或空(支持去两端空字符) | hibernate-validator |
| @Length | 验证字符串长度范围[min,max] | hibernate-validator |
| 验证是否为email | hibernate-validator | |
| @EAN | 检测字符序列是否有效 | hibernate-validator |
| @CreditCardNumber | 验证身份认证是否有效 | hibernate-validator |
8.封装常用注解
8.1 手机号
创建验证手机号注解
package com.hui.javalearn.tools.annotation;
import org.hibernate.validator.constraints.CompositionType;
import org.hibernate.validator.constraints.ConstraintComposition;
import org.hibernate.validator.constraints.Length;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Null;
import javax.validation.constraints.Pattern;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@ConstraintComposition(CompositionType.OR)
@Pattern(regexp = "1[3|4|5|6|7|8][0-9]\\d{8}")
@Null
@Length(min = 0, max = 0)
@Documented
@Constraint(validatedBy = {})
@Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@ReportAsSingleViolation
public @interface Phone {
String message() default "手机号校验错误";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
使用注解
@PostMapping("/getUser")
// 使用
public ResponseResult<UserResponse> getUser(@Phone @RequestParam(value = "phone") String phone) {
try {
UserModel userModel = userService.searchUserByPhone(phone);
UserResponse userResponse = UserResponse.model2Response(userModel);
return ResponseResult.success(userResponse);
} catch (Exception e) {
return ResponseResult.error(ResultEnum.ERROR.getCode(), e.getMessage());
}
}











