需结合前两篇《全局异常处理器》+《参数校验》!!!!
之前的参数校验还不够优雅!每次都需要在 controller
层手动获取校验错误,然后再返回参数,一坨一坨的,每次都要手动写未免太难看了。我们将使用全局异常管理器来优化这块,告别每次手动获取校验错误信息再返回。
参数校验不通过,会抛出什么异常?
会了弄清楚 validation
在参数校验不通过时,会抛出具体什么异常,需要将 TestController
中的 /test
接口中的 BindingResult
参数去掉,代码如下:
@PostMapping("/test")
@ApiOperationLog(description = "测试接口")
public Response test(@RequestBody @Validated User user) {
return Response.success();
}
重启项目,通过 Apifox再次请求该接口,就能在控制台看到具体的异常信息了:
可以看到异常类名为 MethodArgumentNotValidException
, 我们可以通过它来获取参数校验相关的错误信息。
全局异常处理器捕获该异常
为了解放双手,我们可以通过全局异常处理器来捕获该异常,统一返回错误信息,改造 GlobalExceptionHandler
类,添加 handleMethodArgumentNotValidException()
方法,代码如下:
/**
* 捕获参数校验异常
* @return
*/
@ExceptionHandler({ MethodArgumentNotValidException.class })
@ResponseBody
public Response<Object> handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
// 参数错误异常码
String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode();
// 获取 BindingResult
BindingResult bindingResult = e.getBindingResult();
StringBuilder sb = new StringBuilder();
// 获取校验不通过的字段,并组合错误信息,格式为: email 邮箱格式不正确, 当前值: '123124qq.com';
Optional.ofNullable(bindingResult.getFieldErrors()).ifPresent(errors -> {
errors.forEach(error ->
sb.append(error.getField())
.append(" ")
.append(error.getDefaultMessage())
.append(", 当前值: '")
.append(error.getRejectedValue())
.append("'; ")
);
});
// 错误信息
String errorMessage = sb.toString();
log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage);
return Response.fail(errorCode, errorMessage);
}
上述代码中,我们通过 @ExceptionHandler
注解捕获了MethodArgumentNotValidException.class
类型的异常,并从异常实体类中获取了 BindingResult
对象,从而获取到具体哪些字段校验不通过,最终组合错误信息并返回。
添加参数错误枚举
上面代码中,还需要添加一个 PARAM_NOT_VALID
枚举值,表示参数错误:
package com.yanxiaosheng.xx.common.enums;
import com.yanxiaosheng.xx.common.exception.BaseExceptionInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author: 闫小生
* @date: 2023-08-15 10:33
* @description: 响应异常码
**/
@Getter
@AllArgsConstructor
public enum ResponseCodeEnum implements BaseExceptionInterface {
// ----------- 通用异常状态码 -----------
...省略
PARAM_NOT_VALID("10001", "参数错误"),
...省略
}
测试看下最终效果
修改 TestController
中的 /test
接口,记住不要添加参数 BindingResult
, 将 MethodArgumentNotValidException
异常统一抛给全局异常管理器来处理:
@PostMapping("/test")
@ApiOperationLog(description = "测试接口")
public Response test(@RequestBody @Validated User user) {
return Response.success();
}
重启项目,通过 Apifox 调用 /test
接口,看看返参效果:
可以看到,返参明确返回了哪些字段校验不通过、当前值、以及注解中填写的提示信息,这样返回的好处是,当和前端联调接口时,前端小伙伴可根据提示信息就知道哪个字段出现了问题,避免扯皮。