entity
包下创建Deparement
实体类@Data
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class Department {
/**
* 主键
*/
private Integer id;
/**
* 父级 ID
*/
private Integer parentId;
/**
* 部门名臣
*/
private String name;
/**
* 成立时间
*/
private LocalDateTime createTime;
}
if - else
) String errorPrefix = "添加失败,";
if (department.getId() != null) {
return errorPrefix + "实体参数异常";
}
if (department.getParentId() == null) {
return errorPrefix + "父机构异常";
}
if (department.getParentId() < 0) {
return errorPrefix + "父机构不存在";
}
if (department.getName() == null || department.getName().equals("")) {
return errorPrefix + "名称不能为空";
}
if (department.getCreateTime() == null) {
department.setCreateTime(LocalDateTime.now());
}else {
if (department.getCreateTime().isAfter(LocalDateTime.now())) {
return errorPrefix + "创建时间不能大于当前时间";
}
}
id 必须是 null (因为id是主键)
parentId 不能为 null,必须大于 0
name 不能为空,长度必须大于 0
createTime 肯定不是未来的时间
validator
的依赖,非web
场景(这里开发场景使用web开发场景,所以我们不单独引入该依赖)。 <dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
<version>2.1.9.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class ResultVO {
/**
* 后端是否处理成功
*/
private boolean success;
/**
* 错误码
*/
private String code;
/**
* 错误消息
*/
private String msg;
/**
* 给前端的返回值
*/
private Object data;
public static ResultVO success() {
ResultVO resultVO = new ResultVO();
resultVO.setSuccess(true);
return resultVO;
}
public static ResultVO success(Object data) {
ResultVO resultVO = new ResultVO();
resultVO.setSuccess(true);
resultVO.setData(data);
return resultVO;
}
public static ResultVO fail(ErrorCode errorCode) {
ResultVO resultVO = new ResultVO();
resultVO.setSuccess(false);
resultVO.setCode(errorCode.getCode());
resultVO.setMsg(errorCode.getMsg());
return resultVO;
}
public static ResultVO fail(ErrorCode errorCode, Object data) {
ResultVO resultVO = new ResultVO();
resultVO.setSuccess(false);
resultVO.setCode(errorCode.getCode());
resultVO.setMsg(errorCode.getMsg());
resultVO.setData(data);
return resultVO;
}
}
enum
包,该包下创建ErrorCode
的枚举类,用来声明一些错误状态码,和错误信息。controller包
,在该包中创建DepartmentController
类,该类声明如下测试方法。DeparementController
中的add
方法。注意下面标红信息。add
方法参数加了@Valid
exception
包,在下面建一个CtrlAdvice
的类,声明如下:@Validated
也是可以的。通俗来讲,就是一个对象里面有另一个对象。‘
在上面的entity
包中新建实体类Employee
,声明如下:
Department
实体类中,加了注解验证),那么,另一个实体类的属性上面的注解能否生效。EmployeeController
,声明addList
方法如下:Department
的属性没有进行校验的问题。在Employee
中的Department
类型的属性上加@Valid
注解。department
上面的也可以被校验了。通俗来讲,一对多就是一个实体类中,有一个另一个实体类的集合。如下面:
@Valid
注解就行。(对于一对多的有两种校验,一种是加在属性上,另一种加在List<@Valid Department>
)项目中,有可能参数校验不在Controller
层中,而是在Service
层进行参数校验。
service
层,并创建一个DepartmentService
的实体类(这个实体类不实现接口,按照规范是:DepartmentServiceImpl
实DepartmentService
接口,这里不这样做,直接DepartmentService
声明为class
,而不是interface
),声明如下:DepartmentController
如下:idea
控制台查看,报错误信息javax.validation.ConstraintViolationException
:exception
包中的CtrlAdvice
类中,声明一个异常处理,来捕获该异常(controller调用service,service层的异常抛给controller,添加的异常处理类,可以处理controller中的异常信息):// Service层的校验抛出ConstraintViolationException
@ExceptionHandler
public ResultVO exceptionHandler(ConstraintViolationException ce){
Map<String,String> collect =new HashMap<>();
ce.getConstraintViolations().forEach(constraintViolation -> {
PathImpl propertyPath = (PathImpl) constraintViolation.getPropertyPath();
NodeImpl leafNode = propertyPath.getLeafNode();
String name = leafNode.getName();
String value = constraintViolation.getMessageTemplate();
collect.put(name,value);
});
return ResultVO.fail(ErrorCode.PARAM_ERROR,collect);
}
IEmployee
接口,在serviceimpl
中创建EmployService
实现类,该类实现IEmployee
接口。此时项目结构如下:IEmployeeService
方法声明如下:EmployeeService
声明如下:EmployeeController
声明如下,访问下面的方法总结: 对于在service
层的校验,如果不实现接口的话,在类上加
@Validated
注解,并且在验证的对象参数前加@Valid
注解。对于有接口,有实现类的情况,@valid
一定加在接口中抽象方法的形参前面,@Validated
可以加在实现类上,也可以加在接口上,不能不加,我习惯加在实现类上。
EmployeeController
实体类做出如下的更改,声明如下:name=刘
,·age=250·,使用postman测试,查看异常信息。使用debug启动。debug
启动,在下面位置打上断点:/* 该方法可以捕捉校验不通过异常的地方一:
Service层的校验抛出ConstraintViolationException(校验service层的方法中使用对象作为参数)示例:
@PostMapping
public ResultVO add(@Valid @RequestBody Employee){} @RequestBody 不是必须的 前台可以直接是
表单数据,到后台用对象接收时,也会被验证。
该方法可以捕捉校验不通过异常的地方二:
在Controller层直接写单的的参数,如果校验不通过,也会走该异常处理:示例
@PutMapping
public ResultVO add(@Length(min = 2,message = "名字不能少于两个字符") String name ,
@Max(value = 200,message = "年龄不能超过200") Integer age)
*/
/* 该方法可以捕捉校验不通过异常的地方一:
Service层的校验抛出ConstraintViolationException(校验service层的方法中使用对象作为参数)示例:
@PostMapping
public ResultVO add(@Valid @RequestBody Employee){} @RequestBody 是必须的 前台如果是
表单数据,到后台用对象接收时,不会被验证。
该方法可以捕捉校验不通过异常的地方二:
在Controller层直接写单的的参数,如果校验不通过,也会走该异常处理:示例
@PutMapping
public ResultVO add(@Length(min = 2,message = "名字不能少于两个字符") String name ,
@Max(value = 200,message = "年龄不能超过200") Integer age)
*/
@ExceptionHandler
public ResultVO exceptionHandler(ConstraintViolationException ce){
Map<String,String> collect =new HashMap<>();
ce.getConstraintViolations().forEach(constraintViolation -> {
PathImpl propertyPath = (PathImpl) constraintViolation.getPropertyPath();
NodeImpl leafNode = propertyPath.getLeafNode();
String name = leafNode.getName();
String value = constraintViolation.getMessageTemplate();
collect.put(name,value);
});
return ResultVO.fail(ErrorCode.PARAM_ERROR,collect);
}
DepartmentController
更改如下,下面的Department实体类还是上面的,上面加了注解校验:(可以看到,当参数是一个对象时,@valid或者@validated可以在该对象参数前面)
异常处理方法,可copy版本:
// 处理controller方法中,用对象接收表单数据,不满足校验注解,所抛出的异常信息
@ExceptionHandler
public ResultVO exceptionHandler(BindException b){
List<ObjectError> allErrors = b.getBindingResult().getAllErrors();
Map<String,String> collect = new HashMap<>();
allErrors.forEach(error->{
FieldError fieldError =(FieldError)error;
collect.put(fieldError.getField(),fieldError.getDefaultMessage());
});
return ResultVO.fail(ErrorCode.PARAM_ERROR,collect);
}
就是校验你想校验的那个或者那些属性。
修改上面的Employee
实体类,声明如下:
在这里插入图片描述
查看该校验被哪个异常方法所捕获。
在这里插入图片描述
name
属性,id
属性,那么如何校验没有分组的属性呢,如上面的age
属性。结论,如果类中的属性,没有被分到组的话,则这些属性都属于是默认组)Employee
实体类,如下:(未完。。。抽时间接着测试)