You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot实体校验失效:Postman可提交不符合规则的JSON

Spring Boot参数校验注解不生效问题排查与解决

我用Java搭建了Spring Boot项目,在User实体类中通过@NotBlank、@Size、@Email等校验注解设置了JSON入参规则,但使用Postman提交不符合规则的JSON时,接口仍接收所有输入值;已添加相关Gradle依赖但问题未解决。

User.java实体类

package com.example.demo.model;

import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.Objects;

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank(message = "Name field cannot be blank.")
    @Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters.")
    private String name;

    @NotBlank(message = "DDD field cannot be blank.")
    @Size(min = 2, max = 2, message = "DDD must have 2 characters.")
    private String ddd;

    @NotBlank(message = "Phone Number field cannot be blank.")
    @Size(min = 8, max = 9, message = "Phone number must have between 8 and 9 digits.")
    private String phoneNumber;

    @NotBlank(message = "Email field cannot be blank.")
    @Email(message = "Invalid email format.")
    @Column(unique = true)
    private String email;

    @NotBlank(message = "Password field cannot be blank.")
    @Size(min = 8, max = 12, message = "Password must be between 8 and 12 characters")
    private String password;

    public User() {
    }

    public User(String name, String ddd, String phoneNumber, String email, String password) {
        this.name = name;
        this.ddd = ddd;
        this.phoneNumber = phoneNumber;
        this.email = email;
        this.password = password;
    }

    // Standard getters and setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public String getPassword() {
        return password;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public String getDdd() {
        return ddd;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) &&
                Objects.equals(email, user.email);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, email);
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", ddd='" + ddd + '\'' +
                ", phoneNumber='" + phoneNumber + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

UserController.java控制器类

package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.example.demo.service.MailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;
    private final MailService mailService;

    @Autowired
    public UserController(UserService userService, MailService mailService) {
        this.userService = userService;
        this.mailService = mailService;
    }

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userService.getAllUsers();
        return new ResponseEntity<>(users, HttpStatus.OK);
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        Optional<User> userOptional = userService.getUserById(id);
        return userOptional
                .map(user -> new ResponseEntity<>(user, HttpStatus.OK))
                .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
        if (userService.existsByEmail(user.getEmail())) {
            return new ResponseEntity<>(HttpStatus.CONFLICT); // Email already exists
        }
        User newUser = userService.saveOrUpdateUser(user);
        return new ResponseEntity<>(newUser, HttpStatus.CREATED);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        Optional<User> userOptional = userService.getUserById(id);
        if (userOptional.isPresent()) {
            // Ensure the ID from the path matches the ID of the user being updated
            user.setId(id);
            User updatedUser = userService.saveOrUpdateUser(user);
            return new ResponseEntity<>(updatedUser, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    @PostMapping("/sendVerificationEmail")
    public ResponseEntity<String> sendVerificationEmail(@RequestParam String email) {
        // Generate a verification code or content if needed
        String verificationContent = "Your verification content here";

        // Use your MailService to send the email
        mailService.sendVerificationEmail(email, "Verification Email", verificationContent);

        return ResponseEntity.ok("Email sent successfully.");
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        if (userService.getUserById(id).isPresent()) {
            userService.deleteUser(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

已添加的Gradle依赖

dependencies {
    implementation 'org.hibernate.validator:hibernate-validator:7.0.2.Final'
    implementation 'org.hibernate:hibernate-core:5.5.6.Final'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}    

问题排查与解决步骤

1. 清理冗余依赖,避免版本冲突

当前依赖同时引入了hibernate-validator、hibernate-core和spring-boot-starter-validation,会导致版本兼容问题。spring-boot-starter-validation已包含了Hibernate Validator的正确依赖,无需单独引入其他两个。修改依赖为:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    // 保留项目其他必要依赖,如spring-boot-starter-data-jpa等
}

2. 补全Update接口的校验注解

updateUser方法未添加@Valid注解,导致更新操作时参数校验不生效。修改该方法:

@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @Valid @RequestBody User user) {
    Optional<User> userOptional = userService.getUserById(id);
    if (userOptional.isPresent()) {
        user.setId(id);
        User updatedUser = userService.saveOrUpdateUser(user);
        return new ResponseEntity<>(updatedUser, HttpStatus.OK);
    }
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

3. 添加全局异常处理器,统一返回校验错误

默认的校验错误响应不够直观,可添加全局异常处理器,返回结构化的错误信息:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }
}

4. 验证Spring Boot版本与注解包的匹配

如果是Spring Boot 3.x版本,校验注解应从jakarta.validation.constraints包导入;Spring Boot 2.x版本则使用javax.validation.constraints包。根据你的代码,当前包导入正确,无需修改。

5. 检查依赖树确认无冲突

执行./gradlew dependencies命令查看项目依赖树,确保spring-boot-starter-validation引入的Hibernate Validator版本与项目其他依赖兼容。


内容的提问来源于stack exchange,提问作者NickVnkn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 15:47:33