前言
最近看到很多童鞋在项目中的对请求参数的校验都用的if来判断各参数的属性,如:
if(StringUtils.isBlank(username)){ return RR.exception("账号不能为空"); } if(StringUtils.isBlank(password)){ return RR.exception("密码不能为空"); } if(StringUtils.isBlank(realName)){ return RR.exception("姓名不能为空"); } ......
每个参数都需要这样一个个去校验null,返回对应信息,代码就像叠罗汉一样~~,在此,楼主强烈推荐一个神器:Validation,有了它,再也不用这样去校验参数啦,可以让我们在项目中不用太关注其他东西,专注于业务逻辑的编写。
引入核心依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
使用示例-添加用户
需要校验的实体类,在此使用了一些常用的校验注解,基本上能够见名知意,每个注解中都有message属性,就是校验不通过后的提示信息
@Data @AllArgsConstructor @NoArgsConstructor @ApiModel(value = "Account", description = "用户实体类") public class Account implements Serializable { private static final long serialVersionUID = -6310331730721778951L; private Long id; @NotBlank(message = "用户名不能为空") @Length(max = 10, message = "用户名最长为10位") private String name; @Pattern(regexp = PatternUtil.PASSWORDREGEX, message = "密码为6-20位数字加字母组合") private String pwd; @NotBlank(message = "姓名不能为空") @Length(max = 8, message = "姓名最长为8位") private String realName; @Pattern(regexp = PatternUtil.PHONENOREGEX, message = "手机号码格式不正确") private String phone; @NotNull(message = "用户性别不能为空") @Max(value = 1,message = "性别 0:男 1:女") @Min(value = 0,message = "性别 0:男 1:女") private Integer sex; @NotNull(message = "用户状态不能为空") @Max(value = 1,message = "用户状态 0:启用 1:禁用") @Min(value = 0,message = "用户状态 0:启用 1:禁用") private Integer status; @Length(max = 200, message = "备注最长为200个字符") private String rmk; }
controller接口,注意在接收参数前需加上@Validated注解,这样就会逐个去校验实体类中需要加了校验注解的的属性,完全通过才进入下一步业务处理,否则抛出MethodArgumentNotValidException异常,在这里我们直接将异常抛出,交给全局异常处理类来处理。
@SysLog(menu = "用户管理", function = "添加",filterParams = {"pwd","password","salt"}) @PermissionCheck("account:manager:add") @PostMapping(value = "/add", produces = BaseConsts.REQUEST_HEADERS_CONTENT_TYPE) @ApiOperation(value = "用户管理-添加用户接口", notes = "用户管理-添加用户接口", httpMethod = BaseConsts.REQUEST_METHOD, response = RR.class) public RR add(@Validated @RequestBody Account account) throws Exception { return RR.success("添加用户成功"); }
声明全局异常处理类,处理所有异常,可以随业务需要将异常种类细分,返回错误码,返回提示信息可自由定义... 这里只需关注MethodArgumentNotValidException异常。
@RestControllerAdvice public class GlobalExceptionHandle { /** * @param e * @return * @Description 未知异常处理 */ @ExceptionHandler(Exception.class) public RR handleException(Exception e) { e.printStackTrace(); return RR.exception("系统异常,请联系管理员"); } /** * @param e * @return * @Description 请求参数异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) public RR handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { return RR.exception("请求参数错误:[" + e.getBindingResult().getFieldError().getDefaultMessage() + "]"); } /** * @param e * @return * @Description 系统业务服务异常 */ @ExceptionHandler(ServerException.class) public RR handleServerException(ServerException e) { return RR.exception(e.getMessage()); } /** * @param e * @return * @Description 令牌校验异常 */ @ExceptionHandler(ExpireException.class) public RR handleExpireException(ExpireException e) { return RR.expire(e.getMessage()); } /** * @param e * @return * @Description 无权访问异常 */ @ExceptionHandler(AccessDeniedException.class) public RR handleAccessDeniedException(AccessDeniedException e) { return RR.forbidden(e.getMessage()); } /** * @param e * @return * @Description 自定义重复操作异常 */ public RR handleRepeatHandleException(RepeatHandleException e) { return RR.exception("您的请求已发送,请勿重复操作!"); } /** * @param e * @return * @Description 登录异常处理 */ @ExceptionHandler(AuthenticationException.class) public RR handleAuthenticationException(AuthenticationException e) { return RR.exception(e.getMessage()); } }
然后我们用Swagger来测试一下接口
1.用户名为空
2.手机号格式错误
通过返回结果可以看到,我们的校验注解已经帮我们按照指定的校验方式校验了指定的字段属性,我们在统一的全局异常处理类中将提示信息封装成需要的返回结果就可以了。
分组校验
有的童鞋在此可能有疑问了,上述方法虽然可行,但我在新增用户和修改用户两个接口中,新增用户的用户id是自动生成的,无需校验;修改用户的用户id是必传的,则需校验。在此业务场景中,那岂不是需要声明两个实体类,但这两个实体类中的属性又大致相同,这不是增加麻烦吗?其实不然,在此我们可以用到groups属性来解决此场景下的问题。
声明分组
注意:在声明分组的时候尽量加上 extend javax.validation.groups.Default 否则,在你声明@Validated(Update.class)的时候,就会出现你在默认没添加groups = {}的时候的校验组@Email(message = "邮箱格式不对"),会不去校验,因为默认的校验组是groups = {Default.class}。
/** * 数据新增分组 */ public interface Create extends Default { } /** * 数据更新分组 */ public interface Update extends Default { }
校验注解中添加分组,groups 为一个数组,可以添加多个分组
@NotNull(message = "id不能为空" , groups = Update.class) private Long id; @NotBlank(message = "用户名不能为空" , groups = {Create.class, Update.class}) @Length(max = 10, message = "用户名最长为10位") private String name;
修改Controller中的@Validated注解,声明校验分组
@PermissionCheck("account:manager:add") @PostMapping(value = "/add", produces = BaseConsts.REQUEST_HEADERS_CONTENT_TYPE) @ApiOperation(value = "用户管理-添加用户接口", notes = "用户管理-添加用户接口", httpMethod = BaseConsts.REQUEST_METHOD, response = RR.class) public RR add(@Validated(Create.class) @RequestBody Account account) throws Exception { return RR.success("添加用户成功"); } @PermissionCheck("account:manager:edit") @PostMapping(value = "/edit", produces = BaseConsts.REQUEST_HEADERS_CONTENT_TYPE) @ApiOperation(value = "用户管理-修改用户接口", notes = "用户管理-修改用户接口", httpMethod = BaseConsts.REQUEST_METHOD, response = RR.class) public RR edit(@Validated(Update.class) @RequestBody Account account) throws Exception { return RR.success("编辑用户成功"); }
使用相同的请求参数测试一下添加用户和修改用户的接口
测试添加接口
测试修改接口
通过接口的返回结果可以看到,新增用户的接口并没有校验id这个参数了,而修改用户的接口中则校验了id不能为空。
常用校验注解
以上就是楼主在项目中使用validation的总结归纳,下面收集了一些常用注解,红色的标注则是楼主在项目中用的比较多的。
JSR提供的校验注解: @Null 被注释的元素必须为 null @NotNull 被注释的元素必须不为 null @AssertTrue 被注释的元素必须为 true @AssertFalse 被注释的元素必须为 false @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @Size(max=, min=) 被注释的元素的大小必须在指定的范围内 @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内 @Past 被注释的元素必须是一个过去的日期 @Future 被注释的元素必须是一个将来的日期 @Pattern(regex) 被注释的元素必须符合指定的正则表达式 Hibernate Validator提供的校验注解: @NotBlank() 验证字符串非null且非空格,长度必须大于0 @Email 被注释的元素必须是电子邮箱地址 @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内 @NotEmpty 被注释的字符串的必须非空 @Range(min=,max=,message=) 被注释的元素必须在合适的范围内
参考博文:https://blog.csdn.net/u013815546/article/details/77248003/