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

Spring Validation失效:请求体字段全为null致校验失败

问题:Spring Boot注册接口请求体无法绑定,所有字段为null
  • 注册接口接收带校验注解的RegisterDto作为RequestBody
  • 即使请求体字段完整,接口返回400 Bad Request,日志显示RegisterDto所有字段为null
  • 移除@Valid注解后返回500错误,原因仍是字段为null
  • 尝试替换为Spring原生@RequestBody、手动编写Getter/Setter和构造器,问题未解决

日志信息

2023-04-10T12:50:57.981+02:00  WARN 1836 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity<java.lang.Object> app.dcuobroker.backend.boundary.v1.api.auth.AuthApiImpl.registerUser(app.dcuobroker.backend.boundary.v1.dto.auth.RegisterDto,jakarta.servlet.http.HttpServletRequest,java.util.Locale) with 6 errors: [Field error in object 'registerDto' on field 'username': rejected value [null]; codes [NotNull.registerDto.username,NotNull.username,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registerDto.username,username]; arguments []; default message [username]]; default message [darf nicht null sein]] [Field error in object 'registerDto' on field 'password': rejected value [null]; codes [NotBlank.registerDto.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registerDto.password,password]; arguments []; default message [password]]; default message [darf nicht leer sein]] [Field error in object 'registerDto' on field 'username': rejected value [null]; codes [NotBlank.registerDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registerDto.username,username]; arguments []; default message [username]]; default message [darf nicht leer sein]] [Field error in object 'registerDto' on field 'email': rejected value [null]; codes [NotNull.registerDto.email,NotNull.email,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registerDto.email,email]; arguments []; default message [email]]; default message [darf nicht null sein]] [Field error in object 'registerDto' on field 'password': rejected value [null]; codes [NotNull.registerDto.password,NotNull.password,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registerDto.password,password]; arguments []; default message [password]]; default message [darf nicht null sein]] [Field error in object 'registerDto' on field 'email': rejected value [null]; codes [NotBlank.registerDto.email,NotBlank.email,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registerDto.email,email]; arguments []; default message [email]]; default message [darf nicht leer sein]] ]

RegisterDto代码

@Data
public class RegisterDto {
    @NotNull
    @NotBlank
    @Size(min = 3, max = 20)
    @Pattern(regexp = "^(?=.{3,20}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$")
    private String username;

    @NotNull
    @NotBlank
    @Email
    @Size(max = 50)
    private String email;

    @NotNull
    @NotBlank
    @Size(min = 8, max = 50)
    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$")
    private String password;

    @JsonProperty("first_name")
    @Size(min = 1, max = 50)
    @Pattern(regexp = "/^[a-z ,.'-]+$/i")
    private String firstName;

    @JsonProperty("last_name")
    @Size(min = 1, max = 50)
    @Pattern(regexp = "/^[a-z ,.'-]+$/i")
    private String lastName;
}

请求体

{
    "email": "test.user@example.com",
    "username": "testuser",
    "password": "TestPw1!",
    "first_name": "Some",
    "last_name": "Name"
}

接口代码

@Operation(summary = "Register a user using the details of the provided dto")
@ApiResponse(responseCode = "200", description = "User was registered successfully",
        content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                schema = @Schema(implementation = UserDto.class))})
@ApiResponse(responseCode = "400", description = "Invalid request body",
        content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                schema = @Schema(implementation = ErrorDto.class))})
@ApiResponse(responseCode = "409", description = "Username or email is already in use",
        content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                schema = @Schema(implementation = ErrorDto.class))})
@PostMapping(value = "/register", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Object> registerUser(
        @RequestBody(description = "Dto used to register the user", required = true,
                content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                        schema = @Schema(implementation = RegisterDto.class)))
        @Valid RegisterDto register,
        HttpServletRequest request,
        Locale locale);

排查与解决方法

1. 修正正则表达式问题

  • password字段的正则表达式中,\d需转义为\\d(Java字符串转义规则),否则会导致Jackson反序列化失败
  • firstName和lastName的@Pattern注解中,移除JS风格的前后斜杠/.../i,改用Java的flags参数指定大小写不敏感:
    @Pattern(regexp = "^[a-z ,.'-]+$", flags = Pattern.Flag.CASE_INSENSITIVE)
    

2. 区分Spring与Swagger的@RequestBody注解

接口中混用了Swagger的@io.swagger.v3.oas.annotations.parameters.RequestBody和Spring的@org.springframework.web.bind.annotation.RequestBody,需明确标注:

@PostMapping(value = "/register", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Object> registerUser(
        @io.swagger.v3.oas.annotations.parameters.RequestBody(
                description = "Dto used to register the user", required = true,
                content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                        schema = @Schema(implementation = RegisterDto.class)))
        @Valid @org.springframework.web.bind.annotation.RequestBody RegisterDto register,
        HttpServletRequest request,
        Locale locale);

3. 验证Lombok与Jackson依赖

  • 确保spring-boot-starter-web依赖已引入(默认包含Jackson),若手动管理依赖,需添加:
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    
  • 确认Lombok依赖正确引入,且IDE已启用Lombok注解处理:
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    

4. 手动测试反序列化

编写简单测试验证Jackson能否正常解析请求体:

ObjectMapper mapper = new ObjectMapper();
String json = "{\"email\":\"test.user@example.com\",\"username\":\"testuser\",\"password\":\"TestPw1!\",\"first_name\":\"Some\",\"last_name\":\"Name\"}";
RegisterDto dto = mapper.readValue(json, RegisterDto.class);
System.out.println(dto.getUsername()); // 输出testuser则反序列化正常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 02:25:02