Spring下的数据校验体系
由于项目中经常需要对数据进行校验,我们下面将对spring提供的数据校验机制进行详解。
在论述spring提供的校验机制前,首先简述 JSR303/JSR-349,hibernate validation,spring validation 之间的关系。JSR303 是一项标准,JSR-349 是其的升级版本,添加了一些新特性,他们规定一些校验规范即校验注解,如 @Null,@NotNull,@Pattern,他们位于 javax.validation.constraints 包下,只提供规范不提供实现。而 hibernate validation 是对这个规范的实践(不要将 hibernate 和数据库 orm 框架联系在一起),他提供了相应的实现,并增加了一些其他校验注解,如 @Email,@Length,@Range 等等,他们位于 org.hibernate.validator.constraints 包下。而万能的 spring 为了给开发者提供便捷,对 hibernate validation 进行了二次封装,显示校验 validated bean 时,你可以使用 spring validation 或者 hibernate validation,而 spring validation 另一个特性,便是其在 springmvc 模块中添加了自动校验,并将校验信息封装进了特定的类中。这无疑便捷了我们的 web 开发。本文主要介绍在 springmvc 中自动校验的机制。
由于hibernate validation 是java校验标准的最好实践,当我们在做spring-web项目时,spring就已经自动引入了相关包,所以我们不需要引入任何的jar包了。为方便大家的理解,我们将从以下几个方面给大家讲解。
1、基本注解
我们在项目中可以灵活应用上述注解完成基本的功能。具体功能演示如下:
public class Foo { @NotBlank private String name; @Min(18) private Integer age; @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误") @NotBlank(message = "手机号码不能为空") private String phone; @Email(message = "邮箱格式错误") private String email; //... getter setter }
验证完毕后的错误信息将会放到中,可以通过提取到错误信息。
2、自定义校验
由于基本的校验注解满足不了我们日常工作的需要,可能需要自己编写校验逻辑。我们首先编写注解,然后编写校验实现工具类。
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Documented @Constraint(validatedBy = {CannotHaveBlankValidator.class})<1> public @interface CannotHaveBlank { // 默认错误消息 String message() default "不能包含空格"; // 分组 Class<?>[] groups() default {}; // 负载 Class<? extends Payload>[] payload() default {}; // 指定多个时使用 @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE}) @Retention(RUNTIME) @Documented @interface List { CannotHaveBlank[] value(); } } // 校验逻辑实现工具类 public class CannotHaveBlankValidator implements <1> ConstraintValidator<CannotHaveBlank, String> { @Override public void initialize(CannotHaveBlank constraintAnnotation) { } @Override public boolean isValid(String value, ConstraintValidatorContext context <2>) { //null 时不进行校验 if (value != null && value.contains(" ")) { <3> // 获取默认提示信息 String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate(); System.out.println("default message :" + defaultConstraintMessageTemplate); // 禁用默认提示信息 context.disableDefaultConstraintViolation(); // 设置提示语 context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation(); return false; } return true; } }
3、异常捕获
在第一部分中可以看到校验逻辑完毕后的错误信息捕获都是在controller类中,如果代码过多那么每个类中均要实现上面的捕获逻辑,则相当的麻烦;
并且代码基本类似,重复性太厉害了。所以,我们结合spring的机制,可以自定义异常的捕获机制。代码展示如下:
package com.shinho.plrs.front.cores.advice;
import com.shinho.plrs.front.cores.pojo.Response;
import com.shinho.plrs.front.infrastructure.exception.CommonExcepEnums;
import com.shinho.plrs.front.infrastructure.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;
/**
* @description:系统全局的异常处理机制
* @date: 2019/1/16 08:54
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 自定义异常处理机制
*
* @param
* @return
*/
@ExceptionHandler(value = CommonException.class)
@ResponseBody
public Response myErrorHandler(HttpServletRequest request, CommonException e) {
log.error("[{}]接口异常:{}", request.getRequestURI(), e);
return Response.error(e.getErrCode(), e.getErrMsg());
}
/**
* 系统异常处理
*
* @param
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Response errorHandler(HttpServletRequest request, Exception ex) {
log.error("[{}]系统异常:{}", request.getRequestURI(), ex);
Throwable error = ex;
while (error != null && !(error instanceof CommonException)) {
error = error.getCause();
}
if (error instanceof CommonException && error != null) {
return Response.error((CommonException) error);
}
return Response.error(ex.getMessage());
}
/**
* 处理参数校验异常
*
* @param ex
* @return
*/
@ExceptionHandler(BindException.class)
@ResponseBody
public Response handleBindException(HttpServletRequest request, BindException ex) {
log.error("[{}]参数校验异常:{}", request.getRequestURI(), ex);
return Response.error(getErrorMessage(ex));
}
/**
* 处理参数校验异常
*
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public Response handleBindException(HttpServletRequest request, MethodArgumentNotValidException ex) {
log.error("[{}]参数校验异常:{}", request.getRequestURI(),