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

使用@NotNull、@NotBlank和@Valid时仍触发必填校验错误的问题

问题排查:@Valid校验失败但请求体字段有效值存在

我在UserDTO类中使用@NotBlank和@NotNull注解校验firstName、lastName、userRole字段,同时通过@Valid注解嵌套校验CrewDTO。在UserController的signup接口中,通过@RequestBody @Valid接收UserDTO参数,且已通过Postman传入包含对应字段有效值的请求体,但仍收到错误提示:First Name is mandatory, Last Name is mandatory and Role is mandatory,请帮忙排查问题原因。

相关代码

UserDTO

package com.pocketxtra.eventmanager.controller.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.pocketxtra.eventmanager.service.domain.Gender;
import com.pocketxtra.eventmanager.service.domain.Role;
import com.pocketxtra.eventmanager.service.domain.UserType;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserDTO {
    String id;
    private String contact;
    @NotBlank(message = "First name is mandatory")
    private String firstName;
    @NotBlank(message = "Last name is mandatory")
    private String lastName;
    @NotNull(message = "Role is mandatory")
    private Role userRole;
    private UserType userType;
    @Valid
    @JsonProperty("crew")
    private CrewDTO crewDTO;
}

CrewDTO

package com.pocketxtra.eventmanager.controller.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.pocketxtra.eventmanager.service.domain.WeekDay;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class CrewDTO extends UserDTO {
    @NotNull(message = "Date of Birth is mandatory")
    private Date dob;
    @Valid
    @NotNull(message = "Education details is mandatory")
    @JsonProperty("education")
    private EducationDTO educationDTO;
    @NotNull(message = "Primary location is mandatory")
    private String primaryLocation;
}

UserController.signup接口

@PostMapping("/signup")
public ResponseEntity<String> signup(@RequestBody @Valid UserDTO userDTO) {
   // 业务逻辑代码
}

Postman请求体

{
  "contact": "3245345451",
  "firstName": "Aman",
  "lastName": "Kumar",
  "userRole": "CREW",
  "crew": {
    "primaryLocation": "abcdncandkjcn",
    "dob": "1999-09-16T08:33:59.309Z",
    "education": {
      "college": "ABC",
      "course": "BCD",
      "city": "KANPUR"
    }
  }
}

问题原因分析

核心问题在于CrewDTO继承了UserDTO,而UserDTO中使用@Valid注解嵌套校验CrewDTO。当校验触发时,会对CrewDTO的所有字段(包括从UserDTO继承来的firstName、lastName、userRole)进行校验,但请求体中crew对象里并没有这些字段——这些字段是在UserDTO的根层级传入的。因此CrewDTO实例中的继承字段值为null/空,触发了@NotBlank和@NotNull的校验规则,导致返回错误提示。

另外需要额外确认:请求体中的userRole值"CREW"是否和Role枚举类中的枚举值完全匹配(包括大小写),如果枚举值定义为Crew而不是CREW,也会导致反序列化失败,进而触发@NotNull校验失败。

解决方案

方案1:移除CrewDTO对UserDTO的继承(推荐)

CrewDTO作为UserDTO的嵌套字段,不应该继承UserDTO,这会导致字段重复和校验逻辑冲突。修改CrewDTO为独立类:

package com.pocketxtra.eventmanager.controller.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class CrewDTO {
    @NotNull(message = "Date of Birth is mandatory")
    private Date dob;
    @Valid
    @NotNull(message = "Education details is mandatory")
    @JsonProperty("education")
    private EducationDTO educationDTO;
    @NotNull(message = "Primary location is mandatory")
    private String primaryLocation;
}

方案2:调整校验策略(保留继承时的妥协方案)

如果必须保留继承关系,可以在CrewDTO中覆盖父类的校验字段,添加@Null注解允许这些字段为空:

public class CrewDTO extends UserDTO {
    // 覆盖父类字段,允许为空
    @Null
    private String firstName;
    
    @Null
    private String lastName;
    
    @Null
    private Role userRole;
    
    // 其他原有字段...
}

方案3:确认枚举反序列化正确性

检查Role枚举类的定义,确保CREW是有效的枚举值:

public enum Role {
    CREW, ADMIN; // 其他枚举值...
}

如果枚举值是小写或其他格式,可添加@JsonCreator注解支持灵活反序列化:

public enum Role {
    CREW, ADMIN;

    @JsonCreator
    public static Role fromValue(String value) {
        return Role.valueOf(value.toUpperCase());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:37:04