在日常工作开发中,难免会遇到各种各样的参数校验问题,大多数情况下我们都是在Controller层进行手动校验,但是当需要校验的参数比较多时,就会出现一大堆重复且枯燥的代码,每当出现这种情况是,想想都会头疼,例如下面这段代码。
/**
* @Author: zhouzhou
* @CreateTime: 2022-11-10 14:47
* @Version: 1.0
*/
@RestController
public class UserLoginController {
@PostMapping
public SingleResponse<String> login(@RequestBody UserLoginReqDTO userLoginReqDTO){
if (StringUtils.isEmpty(userLoginReqDTO.getUserName())){
return SingleResponse.buildFailure("500","用户名不能为空");
}
if (StringUtils.isEmpty(userLoginReqDTO.getPassword())){
return SingleResponse.buildFailure("500","密码不能为空");
}
if (userLoginReqDTO.getUserName().length() < 5){
return SingleResponse.buildFailure("500","用户名不能小于5位");
}
if (userLoginReqDTO.getPassword().length() < 5){
return SingleResponse.buildFailure("500","密码不能小于5位");
}
// 校验通过走服务层逻辑
//返回结果
return SingleResponse.buildSuccess();
}
}
其实还有更简单优雅的办法帮我们去解决这种问题——Spring Validation。
解决方案
Spring Validation使用Java Api规范(JSR303)定义了Bean对象的校验标准。而hibernate validation对这个规范进行了实现,而Spring Validation是对 hibernate validation的再次封装,用来支持spring mvc参数的自动校验,下面我们就以spring boot项目进行代码演示。
引入依赖
因为作者使用的是spring boot 2.7.x,所以需要手动引入 hibernate validator依赖。如果版本小于2.3.x则无需手动引入
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.7.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cola</groupId>
<artifactId>cola-component-dto</artifactId>
<version>4.0.1</version>
</dependency>
项目实战
使用POST请求接收参数,控制层使用开头中的if进行参数校验,效果如下:
使用Spring validation进行参数校验:
1.首先在数据参数对象DTO中声明校验规则
@Data
public class UserLoginReqDTO {
@NotNull(message = "用户名不能为空")
@Length(min = 5) // 长度最小为5
private String userName;
@NotNull(message = "密码不能为空")
@Length(min = 5)
private String password;
}
2.在方法参数上声明校验注解 @Validated
@PostMapping("/user/login2")
public SingleResponse<String> login2(@RequestBody @Validated UserLoginReqDTO userLoginReqDTO){
System.out.println("进入login2方法-------------");
// 校验通过走服务层逻辑
//返回结果
return SingleResponse.buildSuccess();
}
效果如下:
因为参数校验不通过,服务器抛出异常,可以在服务日志中查看到如下提示
2022-11-10 15:30:58.371 WARN 40924 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.alibaba.cola.dto.SingleResponse<java.lang.String> com.example.demojproect.controller.UserLoginController.login2(com.example.demojproect.dto.request.UserLoginReqDTO) with 2 errors: [Field error in object 'userLoginReqDTO' on field 'password': rejected value [null]; codes [NotNull.userLoginReqDTO.password,NotNull.password,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userLoginReqDTO.password,password]; arguments []; default message [password]]; default message [密码不能为空]] [Field error in object 'userLoginReqDTO' on field 'userName': rejected value [null]; codes [NotNull.userLoginReqDTO.userName,NotNull.userName,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userLoginReqDTO.userName,userName]; arguments []; default message [userName]]; default message [用户名不能为空]] ]
但是在日常工作中,无论服务器异常还是成功都会向前端返回http 200,还有固定的json数据结构,然后前端从返回数据中根据状态判断业务是否成功。这个时候就可以使用全局异常处理对异常信息进行拦截,然后将错误信息封装成正确的返回格式。可以参考作者之前发的文章。
在全局异常处理器中添加如下代码
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response handleBizException(MethodArgumentNotValidException e) {
log.error("BusinessException,code={},value={}", e.getParameter(), e.getMessage());
BindingResult bindingResult = e.getBindingResult();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
// 对未通过校验的字段进行错误信息拼接返回
StringBuilder stringBuilder = new StringBuilder();
for (FieldError fieldError : fieldErrors) {
stringBuilder.append(fieldError.getField()).append("-").append(fieldError.getDefaultMessage());
}
return SingleResponse.buildFailure(String.valueOf(500), stringBuilder.toString());
}
添加全局异常处理之后的效果:
使用GET进行参数校验,注意使用GET请求对参数进行校验需要在Controller上加 @Validated
//请求参数
@GetMapping("/user/userinfo")
public SingleResponse<String> getUserInfo(@NotNull(message = "用户ID不能为空") @Min(value = 1,message = "用户ID不能小于1") Integer userId){
System.out.println("进入获取用户信息方法-------------");
// 校验通过走服务层逻辑
//返回结果
return SingleResponse.buildSuccess();
}
//路径参数
@GetMapping("/user/userinfo/{userId}")
public SingleResponse<String> getUserInfo2(@PathVariable("userId")@NotNull(message = "用户ID不能为空") @Min(value = 1,message = "用户ID不能小于1") Integer userId){
System.out.println("进入获取用户信息方法-------------");
// 校验通过走服务层逻辑
//返回结果
return SingleResponse.buildSuccess();
}
到此Spring validation参数校验的简单使用和结合全局异常处理返回错误信息就到此结束了,感兴趣的小伙伴赶紧去试试吧。