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
相关产品推荐
相关产品推荐

