使用MockMvc的Spring测试抛出ServletException问题求解
解决Spring校验无效表单时抛出ConstraintViolationException而非返回400 Bad Request的问题
问题原因
Spring MVC默认不会自动将jakarta.validation.ConstraintViolationException转换为HTTP 400响应,当方法参数校验失败时,该异常会直接向上抛出,最终被Servlet容器捕获并包装为ServletException。
解决方案
1. 确保控制器方法启用参数校验
首先检查控制器方法,必须在请求体参数上添加@Valid或@Validated注解,这是触发Spring参数校验的前提:
@PostMapping("/forms") public ResponseEntity<?> submitForm(@RequestBody @Valid List<FormUploadDTO> dtos) { // 业务逻辑实现 return ResponseEntity.ok().build(); }
如果使用Spring方法级校验,需在控制器类上添加@Validated注解,并在集合元素上标注@Valid:
@RestController @Validated @RequestMapping(BaseApi.BASE_API) public class FormController { @PostMapping("/forms") public ResponseEntity<?> submitForm(@RequestBody List<@Valid FormUploadDTO> dtos) { // 业务逻辑实现 return ResponseEntity.ok().build(); } }
2. 配置全局异常处理器
若已启用参数校验但异常仍未被转换,需自定义全局异常处理器,捕获ConstraintViolationException并返回400响应:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<Map<String, Object>> handleConstraintViolation(ConstraintViolationException ex) { Map<String, Object> errorDetails = new HashMap<>(); ex.getConstraintViolations().forEach(violation -> { String field = violation.getPropertyPath().toString(); String message = violation.getMessage(); errorDetails.put(field, message); }); return ResponseEntity.badRequest().body(errorDetails); } }
该处理器会将校验错误信息封装为JSON格式,同时返回HTTP 400状态码,匹配测试预期。
3. 优化测试断言(可选)
测试类中可使用MockMvc内置断言方法,替代手动获取MvcResult再判断状态码的方式:
mockMvc.perform(post(BaseApi.BASE_API + "/forms") .contentType(MediaType.APPLICATION_JSON) .content(asJsonString(Collections.singletonList(dto)))) .andDo(print()) .andExpect(status().isBadRequest());
验证效果
完成上述配置后,重新运行测试,提交无效表单时会返回HTTP 400响应,而非抛出ServletException,测试断言将正常通过。
内容的提问来源于stack exchange,提问作者iiiiiiiiiiiiiiiiiiii
相关产品推荐
相关产品推荐

