pom 文件引入
使用 Validation 前,要先在 pom 中引入相关依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
创建分组
在很多时候,同一个模型可能会在多处被用到,但每处的校验场景又不一定相同。如:新增信息 和 修改信息,参数可能都是 user 模型,在新增时 user 中 name 字段不能为空;在修改时 user 中 name 字段可以为空。这样我们可以用 groups 来实现,同一个模型在不同场景下,动态校验模型中的不同字段。因此先创建 Insert 和 Update 两个分组
package com.test.tools.validation;public interface Insert {}
package com.test.tools.validation;public interface Update {}
在实体类中添加校验信息
package com.test.tools.entity;import com.test.tools.validation.Insert;import lombok.Data;import javax.validation.constraints.Email;import javax.validation.constraints.NotBlank;import javax.validation.constraints.NotNull;@Datapublic class DiffyEntity implements java.io.Serializable{@NotBlank(message = "稳定版本的ip地址不能为空",groups = Insert.class)protected String primaryIp;@NotBlank(message = "稳定版本的端口不能为空",groups = Insert.class)protected String primaryPort;@NotBlank(message = "候选版本的ip地址不能为空",groups = Insert.class)protected String candidateIp;@NotBlank(message = "候选版本的端口不能为空",groups = Insert.class)protected String candidatePort;@NotBlank(message = "邮箱不能为空",groups = Insert.class)@Email(message = "邮箱地址不合法")protected String email;}
通过约束性的注解,可以对参数进行验证,并通过 groups 进行分组校验。
- groups = Insert.class 则当前验证规则作用于 Insert 分组
- groups = Update.class 则当前验证规则作用于 Update 分组
约束性注解说明
| 注解 | 作用 | 使用方法 | | —- | —- | —- | | @NotNull | 不能为null,可以是空 | @NotNull(message=””) | | @Null | 必须是null | @Null(message=””) | | @Email | 必须是email格式 | @Email(message=””) | | @Length | 长度必须在指定范围内 | @Length(min = 5, max = 10, message=””) | | @NotBlank | 字符串不能为null
字符串trim()后也不能等于”” | @NotBlank(message=””) | | @NotEmpty | 不能为null
集合、数组、map等size()不能为0;
字符串trim()后可以等于”” | @NotEmpty(message=””) | | @Range | 值必须在指定范围内 | @Range(min = 100, max = 200, message=””) | | @URL | 必须是一个URL | @URL(message=””) | | @Pattern | 必须满足指定的正则表达式 | @Pattern(regexp = “\\d+”, message=””) | | @Max | 最大不得超过此最大值 | @Max(value = 200, message=””) | | @Min | 最大不得小于此最小值 | @Min(value = 200, message=””) | | @Future | 日期必须在当前日期的未来 | @Future(message=””) | | @Past | 日期必须在当前日期的过去 | @Past(message=””) | | @Digits | 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内 | @Digits(integer = 5, fraction = 3, message=””) | | @DecimalMax | 设置不能超过最大值 | @DecimalMax(value = “12.3”,message=””) | | @DecimalMin | 设置不能超过最小值 | @DecimalMax(value = “12.3”,message=””) | | @AssertFalse | 可以为null,如果不为null的话必须为false | @AssertFalse(message=””) | | @AssertTrue | 可以为null,如果不为null的话必须为true | @AssertTrue(message=””) |
在Controller中使用@Validated注解
在 Controller 接口方法参数之前使用 @Validated 注解启用参数验证,可以赋值不同的分组名称进行分组验证,比如上面的 Insert.class 和 Update.class,分组验证可以用来处理不同接口需要校验不同参数的情况。验证不通过会返回前端 JSON 数据,处理方式有两种:使用 BindingResult 和 全局异常处理
BindingResult 处理
@PostMapping("/post/diffy")public JsonResult save(@Validated(Insert.class) @RequestBody DiffyEntity diffyEntity, BindingResult bindingResult) {if (bindingResult.hasErrors()) {return JsonResult.fail(bindingResult.getFieldErrors().get(0).getDefaultMessage());}return diffyService.save(diffyEntity);}
全局异常处理
我用的是全局的异常处理,且接口返回的是json数据,因此先创建一个JsonResult,用于返回统一格式给到前端
package com.test.tools.vo;public class JsonResult {private int code;private Object data;private String msg;public JsonResult() {}public JsonResult(int code, Object data) {this.code = code;this.data = data;}public JsonResult(int code, Object data, String msg) {this.code = code;this.data = data;this.msg = msg;}public static JsonResult buildSuccess(Object data){return new JsonResult(200,data);}public static JsonResult warn(String msg){return new JsonResult(-1,"",msg);}public static JsonResult fail(String msg){return new JsonResult(-1,"",msg);}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}}
然后再创建一个全局异常处理的类 GlobalExceptionHandler,处理一些通用的异常
package com.test.tools.exception;import com.test.tools.vo.JsonResult;import org.apache.catalina.connector.ClientAbortException;import org.springframework.dao.DataAccessResourceFailureException;import org.springframework.dao.EmptyResultDataAccessException;import org.springframework.dao.InvalidDataAccessApiUsageException;import org.springframework.http.converter.HttpMessageNotReadableException;import org.springframework.transaction.CannotCreateTransactionException;import org.springframework.web.HttpRequestMethodNotSupportedException;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.MissingServletRequestParameterException;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.UnexpectedTypeException;import java.sql.SQLTransientConnectionException;@ControllerAdvicepublic class GlobalExceptionHandler {// 声明要捕获的异常@ExceptionHandler(Error.class)@ResponseBodypublic JsonResult defaultError(HttpServletRequest request, Error e) {if (e instanceof NoClassDefFoundError) {return JsonResult.fail("找不到类错误" + e.getMessage());}// 未知错误e.printStackTrace();return JsonResult.fail("项目运行异常, 请联系技术人员...");}// 声明要捕获的异常@ExceptionHandler(Exception.class)@ResponseBodypublic JsonResult defaultException(HttpServletRequest request, Exception e) {// 请求方式有误if (e instanceof HttpRequestMethodNotSupportedException) {return JsonResult.warn("请求方式POST/GET有误");}/*** @Validated 进行接口参数校验统一处理*/if (e instanceof MethodArgumentNotValidException) {MethodArgumentNotValidException exception = (MethodArgumentNotValidException)e;return JsonResult.warn(exception.getBindingResult().getFieldErrors().get(0).getDefaultMessage());}/*** 缺少参数异常*/if (e instanceof MissingServletRequestParameterException) {return JsonResult.warn("缺少请求参数");}/*** 接口请求不对*/if (e instanceof InvalidDataAccessApiUsageException) {return JsonResult.warn("请检查接口请求是否缺少参数");}// 参数格式有误,比如int类型输入了abcif (e instanceof HttpMessageNotReadableException) {return JsonResult.warn("参数格式异常:" + e.getMessage());}if (e instanceof NumberFormatException) {return JsonResult.warn("参数格式异常" + e.getMessage());}/*** 没有匹配到的数据*/if (e instanceof EmptyResultDataAccessException) {return JsonResult.warn("数据不存在, 请检查参数是否正确");}/*** 前面的都是警告,不输出错误信息*/e.printStackTrace();// id用了@NotBlankif (e instanceof UnexpectedTypeException) {return JsonResult.fail("请求参数类型异常" + e.getMessage());}// 数据库异常if (e instanceof DataAccessResourceFailureException) {return JsonResult.fail("数据库连接异常");}if (e instanceof SQLTransientConnectionException) {return JsonResult.fail("数据库连接异常");}if (e instanceof CannotCreateTransactionException) {return JsonResult.fail("数据库连接异常");}if (e instanceof ClientAbortException) {return JsonResult.fail("中止了一个已建立的连接");}// 空指针异常if (e instanceof NullPointerException) {e.printStackTrace();return JsonResult.fail("空指针异常");}// 未知错误return JsonResult.fail("服务器处理异常, 请联系技术人员...");}}
最后在 Controller 中,使用 @Validated 注解启用参数验证
@PostMapping(path = "/post/diffy")public JsonResult add(@Validated(Insert.class) @RequestBody DiffyEntity diffyEntity,Exception e){// 异常处理,包括参数异常if (e instanceof MethodArgumentNotValidException) {MethodArgumentNotValidException exception = (MethodArgumentNotValidException)e;return JsonResult.warn(exception.getBindingResult().getFieldErrors().get(0).getDefaultMessage());}// 保存提交记录diffyService.addCommitLog(diffyEntity);// 返回启动命令JsonResult jsonResult = new JsonResult(200,diffyService.getStartCommand(),"success");return jsonResult;}
利用 postman 验证一下效果
