1. 首先引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
点标识进去可以发现是通过Hibernate Validator使用 Java Bean Validation
2. 属性上添加校验注解
@NotBlank(message = "姓名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6, max = 16, message = "密码长度为6-16位")
private String password;
@Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}", message = "手机号格式不正确")
private String phone;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不合理")
private String email;
// 嵌套验证必须加 @Valid,否则嵌套中的验证不生效,list中也算嵌套
@Valid
@NotNull(message = "userinfo不能为空")
private UserInfo userInfo;
在方法入参上配合注解@Valid提供嵌套验证功能
public AjaxResult editSave(@Validated @RequestBody GenTable genTable) {
@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。
@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。
3. Validation常用的校验注解
验证注解 | 验证的数据类型 | 说明 |
---|---|---|
@AssertFalse | Boolean,boolean | 验证注解的元素值是false |
@AssertTrue | Boolean,boolean | 验证注解的元素值是true |
@NotNull | 任意类型 | 验证注解的元素值不是null |
@Null | 任意类型 | 验证注解的元素值是null |
@Min(value=值) | BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型 | 验证注解的元素值大于等于@Min指定的value值 |
@Max(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@Max指定的value值 |
@DecimalMin(value=值) | 和@Min要求一样 | 验证注解的元素值大于等于@ DecimalMin指定的value值 |
@DecimalMax(value=值) | 和@Min要求一样 | 验证注解的元素值小于等于@ DecimalMax指定的value值 |
@Digits(integer=整数位数, fraction=小数位数) | 和@Min要求一样 | 验证注解的元素值的整数位数和小数位数上限 |
@Size(min=下限, max=上限) | 字符串、Collection、Map、数组等 | 验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小 |
@Past | java.util.Date,java.util.Calendar;Joda Time类库的日期类型 | 验证注解的元素值(日期类型)比当前时间早 |
@Future | 与@Past要求一样 | 验证注解的元素值(日期类型)比当前时间晚 |
@NotBlank | CharSequence子类型 | 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格 |
@Length(min=下限, max=上限) | CharSequence子类型 | 验证注解的元素值长度在min和max区间内 |
@NotEmpty | CharSequence子类型、Collection、Map、数组 | 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0) |
@Range(min=最小值, max=最大值) | BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子类型和包装类型 | 验证注解的元素值在最小值和最大值之间 |
@Email(regexp=正则表达式,flag=标志的模式) | CharSequence子类型(如String) | 验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式 |
@Pattern(regexp=正则表达式,flag=标志的模式) | String,任何CharSequence的子类型 | 验证注解的元素值与指定的正则表达式匹配 |
@Valid | 任何非原子类型 | 指定递归验证关联的对象如用户对象中有个地址对象属性,如果想在验证用户对象时一起验证地址对象的话,在地址对象上加@Valid注解即可级联验证 |
4. 校验方式
在Controller方法参数前加@Valid注解,校验不通过时直接抛异常。
了解其他校验方式:在Controller方法参数前加@Valid注解,参数后面定义一个BindingResult类型参数——执行时会将校验结果放进bindingResult里面,用户自行判断并处理。
/**
* 将校验结果放进BindingResult里面,用户自行判断并处理
*
* @param userInfo
* @param bindingResult
* @return
*/
@PostMapping("/testBindingResult")
public String testBindingResult(@RequestBody @Valid UserInfo userInfo, BindingResult bindingResult) {
// 参数校验
if (bindingResult.hasErrors()) {
String messages = bindingResult.getAllErrors()
.stream()
.map(ObjectError::getDefaultMessage)
.reduce((m1, m2) -> m1 + ";" + m2)
.orElse("参数输入有误!");
//这里可以抛出自定义异常,或者进行其他操作
throw new IllegalArgumentException(messages);
}
return "操作成功!";
}
或者使用工具类,用户手动调用对应API执行校验,这种方法适用于校验任意一个有valid注解的实体类,并不仅仅是只能校验接口中的参数;
import org.springframework.util.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import java.util.Set;
/**
* 手动调用api方法校验对象
*/
public class MyValidationUtils {
public static void validate(@Valid Object value) {
Set<ConstraintViolation<@Valid Object>> validateSet = Validation.buildDefaultValidatorFactory()
.getValidator()
.validate(value);
if (!CollectionUtils.isEmpty(validateSet)) {
String messages = validateSet.stream()
.map(ConstraintViolation::getMessage)
// 归约实现字符串合并
.reduce((m1, m2) -> m1 + ";" + m2)
.orElse("参数输入有误!");
throw new IllegalArgumentException(messages);
}
}
}
5. 全局异常处理器
@RestControllerAdvice //把信息转成json格式返回
public class GlobalExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return AjaxResult.error(e.getMessage());
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class) //作用于 @Validated @Valid 注解,仅对于表单提交有效,对于以json格式提交将会失效。
public AjaxResult handleBindException(BindException e)
{
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return AjaxResult.error(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class) //作用于 @Validated @Valid 注解,前端提交的方式为json格式有效
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
{
log.error(e.getMessage(), e);
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return AjaxResult.error(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(ConstraintViolationException.class) //作用于 @NotBlank @NotNull @NotEmpty 注解,校验单个String、Integer、Collection等参数异常处理。
public Object handleConstraintViolationException(ConstraintViolationException e)
{
log.error(e.getMessage(), e);
String message = e.getConstraintViolations()
.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining("; "));
return AjaxResult.error(message);
}
}
6. 自定义xss校验注解
自定义注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义xss校验注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Constraint(validatedBy = { XssValidator.class })
public @interface Xss
{
String message()
default "不允许任何脚本运行";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
实现ConstraintValidator接口
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 自定义xss校验注解实现
*/
public class XssValidator implements ConstraintValidator<Xss, String>
{
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext)
{
if (value == null && value.equals("") && value.equals(" "))
{
return true;
}
return !containsHtml(value);
}
public static boolean containsHtml(String value)
{
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
return matcher.matches();
}
}
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
public String getUserName() {
return userName;
}