Spring中StreamingResponseBody不被识别的问题解决咨询
我希望在Spring控制器中返回StreamingResponseBody以实现响应内容流式传输,代码示例如下:
@RestController class MyController(MyService service) { @PostMapping("/test") Object test() { return service.data() } } @Service class MyService { Object data() { File file=new File("Path to the pdf file"); FileInputStream instream=new FileInputStream(file); StreamingResponseBody stream = out -> { instream.transferTo(out) }; return ResponseEntity .ok() .contentType(MediaType.parseMediaType("application/pdf")) .body(stream) } }
运行时出现异常:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class deegixl.framework.file.MyService$2$3$$Lambda$1416/0x00000008019d5240] with preset Content-Type 'application/pdf']
该异常表明Spring已识别到ResponseEntity(尽管返回类型为Object),但未识别StreamingResponseBody。
我明确返回ResponseEntity<StreamingResponseBody>可解决问题,但实际场景中MyService有时需返回POJO用于JSON序列化;将文件转为byte[]也可行,但为保障性能需实现流式传输。请问该如何解决此问题?
解决方案
方法1:让Service返回泛型ResponseEntity
修改MyService的返回类型为ResponseEntity<?>,既支持返回带StreamingResponseBody的流式响应,也能返回带POJO的JSON响应:
@Service class MyService { ResponseEntity<?> data() { // 场景1:返回流式PDF File file = new File("Path to the pdf file"); try (FileInputStream instream = new FileInputStream(file)) { StreamingResponseBody stream = out -> instream.transferTo(out); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) .body(stream); } catch (IOException e) { // 场景2:返回错误POJO ErrorResponse error = new ErrorResponse("文件读取失败", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .contentType(MediaType.APPLICATION_JSON) .body(error); } } } // 控制器直接返回泛型ResponseEntity @RestController class MyController(MyService service) { @PostMapping("/test") ResponseEntity<?> test() { return service.data(); } }
Spring能明确识别ResponseEntity类型,会根据其内部body的实际类型自动选择对应的处理器,不管是流式响应还是POJO序列化都能正常工作。
方法2:控制器层做类型适配
如果不想改动Service的返回类型(保持返回Object),可以在控制器中对返回值做类型判断,手动转换为带明确泛型的ResponseEntity:
@RestController class MyController(MyService service) { @PostMapping("/test") Object test() { Object result = service.data(); if (result instanceof ResponseEntity<?>) { ResponseEntity<?> response = (ResponseEntity<?>) result; if (response.getBody() instanceof StreamingResponseBody) { // 转换为明确泛型的ResponseEntity,让Spring识别流式响应 return ResponseEntity.status(response.getStatusCode()) .headers(response.getHeaders()) .body((StreamingResponseBody) response.getBody()); } } // 其他情况直接返回,Spring自动处理POJO序列化 return result; } }
这种方式无需修改Service代码,仅通过控制器层的类型转换解决识别问题。
方法3:自定义消息转换器(进阶)
如果上述方法无法满足特殊需求,可以自定义HttpMessageConverter,专门处理StreamingResponseBody类型的body,即使返回类型是Object也能被正确识别。不过该方式实现相对复杂,适合有扩展需求的场景。
内容的提问来源于stack exchange,提问作者Javaddict

