目录
一、前言
二、JSR303 简介
三、使用方法
常用注解
@Validated、@Valid区别
四、编写测试代码:
1. 实体类添加校验
2. 统一返回类型
3. 测试类
4.我们把异常返回给页面
5.抽离全局异常处理
2. 书写ExceptionControllerAdvice
一、前言
我们在日常开发中,避不开的就是参数验证,有人说前端不是回在表单证进行校验吗,在后端中,我们可以直接不管前端怎么做判断过滤,在后端中为了安全,还是需要进行判断的。在前端做校验是很容易绕过的,举个例子,当测试使用PpostMan
时,如果后端没有校验,肯定回出现很多异常。今天就和大家来一起学习 JSR303
专门用于参数校验。
二、JSR303 简介
JSR-303
是 JAVA EE 6 中的一项子规范,叫做 Bean Validation
,官方参考实现是Hibernate Validator
。Hibernate Validator
提供了 JSR 303
规范中所有内置 constraint
的实现,除此之外还有一些附加的 cons
traint
。
三、使用方法
在 SpringBoot
项目的 pom.xml
文件中导入 JSR303
数据校验的启动依赖
创建 SpringB
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>3.0.1</version></dependency>
oot 的方式,有兴趣的朋友可以参考SpringBoot 快速入门(保姆级详细教程)
pom.xml
文件:
常用注解
@Validated、@Valid区别
@Validated:
-
Spring提供的
-
支持分组校验
-
可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
-
由于无法加在成员属性(字段)上,所以无法单独完成级联校验,需要配合@Valid
@Valid:
-
JDK提供的(标准JSR-303规范)
-
不支持分组校验
-
可以用在方法、构造函数、方法参数和成员属性(字段)上
-
可以加在成员属性(字段)上,能够独自完成级联校验
总结:@Validated
用到分组时使用,一个学校对象里还有很多个学生对象需要使用
@Validated在Controller
方法参数前加上
,@Valid
加在学校中的学生属性上,不加则无法对学生对象里的属性进行校验!
四、编写测试代码:
1. 实体类添加校验
package com.javaClass.entity;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
@Data
public class BrandEntity implements Serializable {private static final long serialVersionUID = 1L;
/*** 品牌id*/@NotNull(message = "修改必须有品牌id")private Long brandId;/*** 品牌名F*/@NotBlank(message = "品牌名必须提交")private String name;/*** 品牌logo地址*/@NotBlank(message = "地址必须不为空")private String logo;/*** 介绍*/private String descript;
/*** 检索首字母*///正则表达式@Pattern(regexp = "^[a-zA-Z]$",message = "检索的首字母必须是字母")private String firstLetter;/*** 排序*/@Min(value = 0,message = "排序必须大于等于0")private Integer sort;
}
2. 统一返回类型
import com.alibaba.druid.util.StringUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;//统一返回结果
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel
public class Result<T> {@ApiModelProperty("响应码")private Integer code;@ApiModelProperty("相应信息")private String msg;@ApiModelProperty("返回对象或者集合")private T data;//成功码public static final Integer SUCCESS_CODE = 200;//成功消息public static final String SUCCESS_MSG = "SUCCESS";//失败public static final Integer ERROR_CODE = 201;public static final String ERROR_MSG = "系统异常,请联系管理员";//没有权限的响应码public static final Integer NO_AUTH_COOD = 999;//执行成功public static <T> Result<T> success(T data){return new Result<>(SUCCESS_CODE,SUCCESS_MSG,data);}//执行失败public static <T> Result failed(String msg){msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;return new Result(ERROR_CODE,msg,"");}//传入错误码的方法public static <T> Result failed(int code,String msg){msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;return new Result(code,msg,"");}//传入错误码的数据public static <T> Result failed(int code,String msg,T data){msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;return new Result(code,msg,data);}
}
3. 测试类
package com.javaClass.zcm.controller;
import com.javaClass.zcm.entity.BrandEntity;
import com.javaClass.zcm.utils.Result;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("/hello")
public class testController {
@PostMapping("/add")public Result add(@Valid @RequestBody BrandEntity brandEntity) {
return Result.success("成功");}
}
通过 postMan 进行测试
首次测试结果如下:
4.我们把异常返回给页面
@PostMapping("/add")
public Result add(@Valid @RequestBody BrandEntity brandEntity, BindingResult bindingResult){if (bindingResult.hasErrors()){Map<String,String> map = new HashMap<>();bindingResult.getFieldErrors().forEach(item ->{map.put(item.getField(),item.getDefaultMessage());});return Result.failed(400,"提交的数据不合规范",map);}return Result.success("成功");
}
这次测试结果如下:
5.抽离全局异常处理
1. 心得体会
上面我们要在每个校验的接口上面写,所以我们要抽离出来做个全局异常。并且要改进一下,原来的是把错误信息放到data里,但是正常情况下的data是返回给前端的数据。我们这样把异常数据放进去,会使data的数据有二义性。这样对于前端就不知道里面是数据还是报错信息了哈,这样就可以直接前端展示msg里面的提示即可!
2. 书写ExceptionControllerAdvice
package com.javaClass.zcm.config;
import com.javaClass.zcm.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice(basePackages = "com.wang.test.demo.controller")
public class ExceptionControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)public Result handleVaildException(MethodArgumentNotValidException e){
log.error("数据校验出现问题:{},异常类型:{}",e.getMessage(),e.getClass());BindingResult bindingResult = e.getBindingResult();StringBuffer stringBuffer = new StringBuffer();bindingResult.getFieldErrors().forEach(item ->{//获取错误信息String message = item.getDefaultMessage();//获取错误的属性名字String field = item.getField();stringBuffer.append(field + ":" + message + " ");});return Result.failed(400, stringBuffer + "");
}
@ExceptionHandler(value = Throwable.class)public Result handleException(Throwable throwable){
log.error("错误",throwable);return Result.failed(400, "系统异常");}
}
测试结果:
{"code": 400,"data": "","msg": "logo:地址必须不为空 name:品牌名必须提交 "
}
关注 “JAVA学习课堂” 微信公众号,获取更多学习笔记。