今天我们继续来探究自定义验证规则以及@Validated结合@RestControllerAdvice实现全局异常处理。
首先来看自定义验证规则。假设有一个场景,需要批量解绑手机号和微信, 为了确保请求数据的有效性和一致性,我们应当如何对请求参数进行严格的校验呢?
首先,创建用于数据验证的实体类。
@Data
public class Account{
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@NotBlank(message = "微信号不能为空")
private String weChat;
}
接下来,在controller层,定义处理解绑请求的方法。
@RequestMapping("/account")
@Validated
public class Demo{
@PostMapping("/unbind")
Response<?> unbind(@valid @ValidUnbindAccount @RequestBody List<Account> list){
……
}
}
这里简单聊聊@Validated和@Valid的差异。
- @Validated支持更细粒度的方法参数级别的校验,同时其针对多场景,可以采用分组校验。
- @Valid是面向整个对象做校验。以下是代码示例。
@RequestMapping("/account")
@Validated
public class Demo{
// @Valid针对对象使用
@PostMapping("/unbind")
Response<?> unbind(@valid @ValidUnbindAccount @RequestBody List<Account> list){
……
}
// @Validated 具体参数可用
@GetMapping("/getWechat")
Response<String> getWechat(@NotBlank(message = "用户ID不能为空") String userId){
……
}
}
然后,创建@ValidUnbindAccount注解。
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validateBy = UnbindAccountValidator.class)
public @interface ValidUnbindAccount {
String message() default "",
Class[] group() default {},
Class<? extends Payload>[] payload() default {};
}
紧接着,创建对应的验证器UnbindAccountValidator类。
public class ValidUnbindAccount implements ConstraintValidator<ValidUnbindAccount, List<Account>>{
@Override
public boolean isValid(List<Account> accountList, ConstraintValidatorContext constraintValidatorContext) {
if(ObjectUtils.isEmpty(accountList) || accountList.size() < 1){
return false;
}
// 手机号准确性或者微信号准确性的更多细节校验逻辑可以放到这里, 欢迎大家评论区探讨。
return true;
}
最后,通过@RestControllerAdvice创建全局异常处理类。
@RestControllerAdvice
public class GlobalExceptionHandler{
@ResponseBody
@ExceptionHandler(Throwable.class)
public Response<?> handleException(Throwable ex){
if(e instanceof MethodArgumentNotValidException){
String msg = ex.getBindingResult().getFieldErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","))
return Response.fail(HttpStatus.BAD_REQUEST.value(), msg)
}
if(e instanceOf BizRuntimeException){
return Response.fail(ex.getMessage())
}
}
}
当向/account/unbind接口发送请求时,如果传入的list为空或者list的size< 1都会抛出
MethodArgumentNotValidException
异常。全局异常处理类会捕获这个异常,并将错误信息以 JSON 格式返回给客户端,状态码为400 Bad Request
。此外,如果urI找不到或者请求到达service层处理时发生异常(如数据索引越界,空指针异常等),全局异常处理类业会捕获该异常,并响应给客户端。