Spring Boot中捕获多异常并对应不同HTTP状态码实现方案
实现方案
1. 自定义校验注解(绑定状态码)
给每个校验规则创建带状态码属性的自定义注解,让校验失败时能直接关联对应HTTP状态码,示例如下:
密码长度校验注解
@Target({FIELD}) @Retention(RUNTIME) @Constraint(validatedBy = PasswordLengthValidator.class) public @interface ValidPassword { String message() default "Password should have 16 characters."; int status() default 403; // 绑定403状态码 Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
对应校验器:
public class PasswordLengthValidator implements ConstraintValidator<ValidPassword, String> { @Override public boolean isValid(String password, ConstraintValidatorContext context) { return password != null && password.length() == 16; } }
邮箱格式校验注解
@Target({FIELD}) @Retention(RUNTIME) @Constraint(validatedBy = EmailFormatValidator.class) public @interface ValidEmail { String message() default "Invalid Email Format."; int status() default 422; // 绑定422状态码 Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
对应校验器:
public class EmailFormatValidator implements ConstraintValidator<ValidEmail, String> { private static final String EMAIL_PATTERN = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"; private Pattern pattern = Pattern.compile(EMAIL_PATTERN); @Override public boolean isValid(String email, ConstraintValidatorContext context) { if (email == null) return false; Matcher matcher = pattern.matcher(email); return matcher.matches(); } }
姓名非空校验注解
@Target({FIELD}) @Retention(RUNTIME) @Constraint(validatedBy = NotEmptyNameValidator.class) public @interface ValidName { String message() default "Name cannot be empty."; int status() default 500; // 绑定500状态码 Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
对应校验器:
public class NotEmptyNameValidator implements ConstraintValidator<ValidName, String> { @Override public boolean isValid(String name, ConstraintValidatorContext context) { return name != null && !name.trim().isEmpty(); } }
2. 在用户DTO中使用自定义注解
public class UserCreateDTO { @ValidPassword private String password; @ValidEmail private String email; @ValidName private String name; // 生成getter、setter方法 }
3. 解析BindingResult生成指定格式响应
在Controller的创建用户方法中,遍历BindingResult的错误信息,通过反射获取字段上的自定义注解,提取对应状态码:
@PostMapping("/users") public ResponseEntity<?> createUser(@Valid @RequestBody UserCreateDTO userDTO, BindingResult bindingResult) { if (bindingResult.hasErrors()) { List<Mistake> mistakes = new ArrayList<>(); for (ObjectError error : bindingResult.getAllErrors()) { if (error instanceof FieldError fieldError) { try { Field field = UserCreateDTO.class.getDeclaredField(fieldError.getField()); // 匹配对应注解并提取状态码 ValidPassword validPassword = field.getAnnotation(ValidPassword.class); if (validPassword != null && validPassword.message().equals(error.getDefaultMessage())) { mistakes.add(new Mistake(String.valueOf(validPassword.status()), error.getDefaultMessage())); continue; } ValidEmail validEmail = field.getAnnotation(ValidEmail.class); if (validEmail != null && validEmail.message().equals(error.getDefaultMessage())) { mistakes.add(new Mistake(String.valueOf(validEmail.status()), error.getDefaultMessage())); continue; } ValidName validName = field.getAnnotation(ValidName.class); if (validName != null && validName.message().equals(error.getDefaultMessage())) { mistakes.add(new Mistake(String.valueOf(validName.status()), error.getDefaultMessage())); } } catch (NoSuchFieldException e) { e.printStackTrace(); } } } return ResponseEntity.badRequest().body(Map.of("mistakes", mistakes)); } // 执行用户创建逻辑 return ResponseEntity.ok("User created successfully"); }
4. 定义错误实体类
public class Mistake { private String status; private String detail; public Mistake(String status, String detail) { this.status = status; this.detail = detail; } // 生成getter、setter方法 }
简化方案(适合简单场景)
如果不想自定义过多注解,可直接通过错误消息映射状态码:
// 定义消息与状态码的映射关系 private static final Map<String, String> MESSAGE_STATUS_MAP = new HashMap<>(); static { MESSAGE_STATUS_MAP.put("Password should have 16 characters.", "403"); MESSAGE_STATUS_MAP.put("Invalid Email Format.", "422"); MESSAGE_STATUS_MAP.put("Name cannot be empty.", "500"); } // 在Controller中使用 for (ObjectError error : bindingResult.getAllErrors()) { String status = MESSAGE_STATUS_MAP.getOrDefault(error.getDefaultMessage(), "400"); mistakes.add(new Mistake(status, error.getDefaultMessage())); }
内容的提问来源于stack exchange,提问作者R3bck
相关产品推荐
相关产品推荐

