如何让Spring MVC参数校验失败统一返回400状态码?
如何统一Spring MVC参数校验失败的状态码为400?
我完全懂你的困扰——Spring MVC默认对不同类型参数的校验失败返回不同状态码,确实有点反直觉:@RequestBody校验不通过返回400,而@RequestHeader、@PathVariable、@RequestParam这些参数校验失败却返回500,这对前端统一处理错误响应很不友好。不过别担心,我们可以通过自定义全局异常处理器来彻底解决这个问题。
为什么会出现这种差异?
先简单说下背后的原因:
@RequestBody的校验是在Spring的消息转换器(MessageConverter)解析请求体时触发的,校验失败会抛出MethodArgumentNotValidException,Spring默认对这个异常的处理就是返回400 Bad Request。- 而
@RequestHeader、@RequestParam这类参数的校验,是因为你在Controller上加了@Validated注解,触发了Spring对方法参数的校验,失败时会抛出ConstraintViolationException,这个异常Spring默认会返回500 Internal Server Error。
解决方案:自定义全局异常处理器
我们只需要写一个全局异常处理器,捕获这两类校验异常,统一返回400状态码,还可以顺便整理友好的错误信息。
全局异常处理器代码示例
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.HashMap; import java.util.Map; @RestControllerAdvice public class GlobalValidationExceptionHandler { // 处理@RequestBody参数校验失败的异常 @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Map<String, String>> handleRequestBodyValidation(MethodArgumentNotValidException ex) { Map<String, String> errorDetails = new HashMap<>(); // 提取字段级错误信息 ex.getBindingResult().getAllErrors().forEach(error -> { String fieldName = ((FieldError) error).getField(); String errorMsg = error.getDefaultMessage(); errorDetails.put(fieldName, errorMsg); }); return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); } // 处理@RequestHeader/@RequestParam/@PathVariable参数校验失败的异常 @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<Map<String, String>> handleMethodParamValidation(ConstraintViolationException ex) { Map<String, String> errorDetails = new HashMap<>(); // 提取参数级错误信息 for (ConstraintViolation<?> violation : ex.getConstraintViolations()) { // 从属性路径中提取参数名,比如"doAction.headerValue.value" -> "headerValue" String paramPath = violation.getPropertyPath().toString(); String paramName = paramPath.substring(paramPath.lastIndexOf('.') + 1); errorDetails.put(paramName, violation.getMessage()); } return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); } }
说明
@RestControllerAdvice:这个注解会让Spring把这个类识别为全局异常处理器,并且自动处理返回JSON格式的响应(如果用@ControllerAdvice的话,需要配合@ResponseBody)。- 两个
@ExceptionHandler方法分别对应两类校验异常:MethodArgumentNotValidException:处理@RequestBody的校验失败,我们可以提取字段的错误信息返回给前端。ConstraintViolationException:处理@Validated标注的Controller中,@RequestHeader、@RequestParam等参数的校验失败,这里我们从异常中提取参数名和错误信息。
- 两个方法都返回
HttpStatus.BAD_REQUEST(即400状态码),完美统一了校验失败的响应状态。
额外提示
- 确保这个异常处理器类被Spring扫描到:可以放在Spring Boot主类所在的包或子包下,或者通过
@ComponentScan指定扫描路径。 - 如果你的项目中还有表单参数绑定的校验(比如
@ModelAttribute参数),可能会抛出BindException,可以在处理器中添加对应的处理方法,同样返回400即可。
这样修改后,不管是哪种参数类型的校验失败,都会返回400状态码,前端就可以统一处理这类错误啦!
内容的提问来源于stack exchange,提问作者oberlies
相关产品推荐
相关产品推荐

