Spring Boot REST控制器集成测试返回406而非预期500问题
这个问题的核心原因是接口声明的返回媒体类型和异常场景下实际返回的内容类型不匹配,导致Spring MVC无法找到合适的消息转换器,从而返回406 Not Acceptable状态码。
问题拆解
你的控制器端点明确声明了produces = "application/zip",意味着正常流程返回的是zip格式的字节流。但当抛出ZipException时,你返回的是RestError对象——这是一个JSON结构的错误数据,此时Spring MVC尝试将RestError转换为application/zip类型,找不到对应的转换器,就会触发406错误。
解决方案
方案1:扩展接口支持的媒体类型
修改控制器的produces属性,同时支持application/zip和application/json,让Spring在异常场景下可以选择合适的媒体类型返回错误信息:
@GetMapping(path = "/{id}/export", produces = {"application/zip", "application/json"}) public ResponseEntity<?> export(@ApiParam(required = true) @PathVariable(value = "id") String id) throws IOException { try { MyDTO myDTO = myService.getDTO(id); byte[] zippedData = zipService.createZip(myDTO.getBytes()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"name.zip\""); return new ResponseEntity<>(zippedData, httpHeaders, HttpStatus.OK); } catch (ZipException e) { // 显式指定错误响应的Content-Type为JSON HttpHeaders errorHeaders = new HttpHeaders(); errorHeaders.setContentType(MediaType.APPLICATION_JSON); return new ResponseEntity<>(e.getRestError(), errorHeaders, HttpStatus.INTERNAL_SERVER_ERROR); } }
注意:将返回类型从ResponseEntity<byte[]>改为ResponseEntity<?>,兼容不同类型的返回值。
方案2:使用全局异常处理器(更优雅的方式)
通过@ControllerAdvice统一处理异常,避免在控制器中重复编写异常逻辑,同时明确指定错误响应的媒体类型:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ZipException.class) public ResponseEntity<RestError> handleZipException(ZipException ex) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return new ResponseEntity<>(ex.getRestError(), headers, HttpStatus.INTERNAL_SERVER_ERROR); } }
然后简化控制器代码,移除局部异常捕获,让全局处理器接管:
@GetMapping(path = "/{id}/export", produces = {"application/zip", "application/json"}) public ResponseEntity<byte[]> export(@ApiParam(required = true) @PathVariable(value = "id") String id) throws IOException, ZipException { MyDTO myDTO = myService.getDTO(id); byte[] zippedData = zipService.createZip(myDTO.getBytes()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"name.zip\""); return new ResponseEntity<>(zippedData, httpHeaders, HttpStatus.OK); }
方案3:调整测试用例的请求头(辅助验证)
确保测试请求明确接受JSON类型(即使使用MediaType.ALL,显式声明也能避免潜在的匹配问题):
mockMvc.perform(get("/{id}/export", "id") .accept(MediaType.ALL) .accept(MediaType.APPLICATION_JSON)) .andDo(print()) .andExpect(status().is5xxServerError());
验证逻辑
完成上述修改后,Spring MVC在异常场景下会自动选择application/json作为响应媒体类型,使用JSON转换器序列化RestError对象,从而返回预期的500状态码和错误内容,不再触发406错误。
内容的提问来源于stack exchange,提问作者Space Cadet

