如何验证@Email注解?Spring Boot邮箱校验异常处理咨询
搞定@Email校验和异常处理的正确姿势
1. 先让@Email注解在Controller层生效
你的@Email是Bean Validation的校验注解,之前没生效是因为没在Controller触发校验,而且JPA会在持久化时才校验,导致异常抛得晚。要让校验提前到请求入口:
首先补全实体类的校验注解(加个非空校验更严谨):
@Entity @Table(name = "users") public class Register { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @NotNull(message = "邮箱不能为空") @Email(message = "请输入合法的邮箱地址") private String email; // getter、setter不变 public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
然后给Controller的@RequestBody参数加@Valid注解,触发校验:
@PutMapping(value = "register") public Map<String, Object> register(@Valid @RequestBody Register data) { return registerService.registerUser(data); }
2. 异常处理别瞎传参数
之前你把MethodArgumentNotValidException直接写进Controller方法参数里,Spring根本不会把异常对象传进去,这就是参数不匹配的原因。正确的做法是用异常处理器捕获校验失败的异常:
方法一:Controller内部处理
在RegisterController里加个异常处理方法:
@ExceptionHandler(MethodArgumentNotValidException.class) public Map<String, Object> handleValidationErrors(MethodArgumentNotValidException ex) { Map<String, Object> response = new HashMap<>(); Map<String, String> errors = new HashMap<>(); // 把所有校验错误转成键值对 ex.getBindingResult().getAllErrors().forEach(error -> { String field = ((FieldError) error).getField(); String msg = error.getDefaultMessage(); errors.put(field, msg); }); response.put("success", false); response.put("errors", errors); return response; }
方法二:全局异常处理(更省心,所有Controller通用)
单独写个全局异常处理器类,一次性处理所有接口的校验异常:
@RestControllerAdvice public class GlobalExceptionHandler { // 处理Controller层参数校验异常 @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Map<String, Object>> handleValidationExceptions(MethodArgumentNotValidException ex) { Map<String, Object> response = new HashMap<>(); Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach(error -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); response.put("success", false); response.put("errors", errors); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } // 处理JPA持久化时的校验异常(防止漏网之鱼) @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<Map<String, Object>> handleConstraintViolation(ConstraintViolationException ex) { Map<String, Object> response = new HashMap<>(); Map<String, String> errors = new HashMap<>(); ex.getConstraintViolations().forEach(violation -> { String field = violation.getPropertyPath().toString(); String msg = violation.getMessage(); errors.put(field, msg); }); response.put("success", false); response.put("errors", errors); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } }
3. 为啥之前的if判断没用?
你写的if (data.getEmail() != null)只判断了非空,但没校验格式。而且之前没在Controller层触发校验,JPA会在调用save的时候才校验邮箱格式,所以哪怕你判断了非空,格式不对还是会抛异常。现在加了@Valid,请求一进来就会校验,不合法直接返回错误,根本到不了服务层的save步骤。
4. 服务层改回正常写法
服务层不用再接收异常参数,直接处理校验通过的实体就行:
@Service public class RegisterService { private final RegisterRepository registerRepository; public RegisterService(RegisterRepository registerRepository) { this.registerRepository = registerRepository; } public Map<String, Object> registerUser(Register data) { Map<String, Object> response = new HashMap<>(); try { Register savedUser = registerRepository.save(data); response.put("success", true); response.put("user", savedUser); } catch (Exception e) { response.put("success", false); response.put("message", "注册失败:" + e.getMessage()); } return response; } }
最后提个关键依赖
Spring Boot 2.3版本之后,spring-boot-starter-web不再默认包含validation依赖,得手动加:
<!-- Maven --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
或者Gradle:
implementation 'org.springframework.boot:spring-boot-starter-validation'
内容的提问来源于stack exchange,提问作者xRay
相关产品推荐
相关产品推荐

