如何在JSON请求到达Spring控制器前校验值避免返回400状态码
Spring JSON参数类型不匹配自定义错误返回实现方案
当JSON请求字段类型和实体类定义不匹配时,触发的400错误是Jackson反序列化失败抛出的HttpMessageNotReadableException导致的,此时请求还未进入控制器逻辑,可以通过全局异常处理器捕获该异常,解析错误字段后返回自定义响应,具体实现如下:
1. 核心实现:捕获JSON反序列化异常
使用@RestControllerAdvice定义全局异常处理器,专门处理JSON解析类错误:
import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap; import java.util.Map; @RestControllerAdvice public class GlobalJsonExceptionHandler { @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity<Map<String, Object>> handleJsonFormatError(HttpMessageNotReadableException e) { Map<String, Object> resp = new HashMap<>(); resp.put("code", 400001); resp.put("message", "请求JSON格式错误"); Map<String, String> fieldErrors = new HashMap<>(); // 解析Jackson类型不匹配错误 if (e.getCause() instanceof InvalidFormatException formatException) { for (JsonMappingException.Reference ref : formatException.getPath()) { String field = ref.getFieldName(); String expectType = formatException.getTargetType().getSimpleName(); String actualValue = formatException.getValue().toString(); fieldErrors.put(field, String.format("类型错误,预期为%s,实际输入值为%s", expectType, actualValue)); } } resp.put("fieldErrors", fieldErrors); return new ResponseEntity<>(resp, HttpStatus.BAD_REQUEST); } }
实现效果示例,当请求传入{"username": 1234, "pin": "johndoe"}时,返回的响应结构如下:
{ "code": 400001, "message": "请求JSON格式错误", "fieldErrors": { "username": "类型错误,预期为String,实际输入值为1234", "pin": "类型错误,预期为Integer,实际输入值为johndoe" } }
2. 扩展:搭配业务参数校验
如果需要同时覆盖非格式类的业务校验(比如用户名长度、pin码范围),可以搭配JSR-380参数校验实现:
- 引入校验依赖(Spring Boot 3+需要单独引入,低版本默认包含在web starter中):
<!-- Maven 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> - 给实体类加校验注解:
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; public class CreateUserRequest { @NotBlank(message = "用户名不能为空") @Size(min = 3, max = 20, message = "用户名长度需在3-20位之间") private String username; @NotNull(message = "pin码不能为空") private Integer pin; // 省略getter、setter } - 控制器接口加
@Valid注解开启校验:@PostMapping("/createUser") public String createUser(@RequestBody @Valid CreateUserRequest request) { // 业务逻辑处理 return "ok"; } - 全局异常处理器补充参数校验异常捕获逻辑:
import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Map<String, Object>> handleParamValidateError(MethodArgumentNotValidException e) { Map<String, Object> resp = new HashMap<>(); resp.put("code", 400002); resp.put("message", "请求参数校验失败"); Map<String, String> fieldErrors = new HashMap<>(); for (FieldError error : e.getBindingResult().getFieldErrors()) { fieldErrors.put(error.getField(), error.getDefaultMessage()); } resp.put("fieldErrors", fieldErrors); return new ResponseEntity<>(resp, HttpStatus.BAD_REQUEST); }
注意:以上实现对所有
@RequestBody的JSON接口生效,不需要修改原有接口逻辑,嵌套实体类的字段类型错误也可以正常识别。
内容的提问来源于stack exchange,提问作者Hunter Yavitz
相关产品推荐
相关产品推荐

