Spring Boot下载PDF触发HttpMediaTypeNotAcceptableException问题解析
异常原因解析
核心问题:错误配置了consumes属性
@GetMapping的consumes属性作用是限定请求体的媒体类型,但GET请求本身通常不携带请求体,这个配置从场景上就不适用。- 当你设置
consumes = MediaType.APPLICATION_JSON_VALUE时,Spring MVC会强制要求请求的Content-Type为application/json,但你测试时的GET请求大概率没有携带这个请求头(也不应该携带)。 - 同时你的请求设置了
Accept: application/pdf,此时Spring的内容协商机制会陷入矛盾:一方面接口要求请求体是JSON(但实际没有),另一方面找不到能将byte[]转换为application/pdf的合适消息转换器,最终抛出HttpMediaTypeNotAcceptableException。
简单说就是:你给GET接口加了一个只针对带请求体的POST/PUT等请求的配置,直接打乱了Spring的请求匹配和响应处理逻辑。
两种实现方式的差异
方式一:ResponseEntity<byte[]>(正确配置后)
- 基于Spring MVC的封装特性,通过
ResponseEntity统一管理响应状态码、响应头和响应体 - 正确用法不需要
consumes,只需在响应中指定正确的Content-Type:
@GetMapping("/download-pdf") public ResponseEntity<byte[]> downloadPdf() { // 生成PDF字节流的逻辑 byte[] pdfContent = generatePdf(); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"document.pdf\"") .body(pdfContent); }
- 优势:符合Spring MVC的规范,能和框架的其他特性(如全局异常处理、拦截器)无缝配合,代码更简洁易维护。
方式二:直接操作HttpServletResponse
- 绕开Spring MVC的消息转换器和内容协商机制,直接通过原生Servlet API写入响应流
- 典型写法:
@GetMapping("/download-pdf") public void downloadPdf(HttpServletResponse response) throws IOException { byte[] pdfContent = generatePdf(); response.setContentType(MediaType.APPLICATION_PDF_VALUE); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"document.pdf\""); response.getOutputStream().write(pdfContent); response.getOutputStream().flush(); }
- 优势:能对响应做更精细化的控制(比如自定义流的写入逻辑);劣势:脱离Spring MVC的封装,无法利用框架的统一处理能力,代码相对繁琐。
内容的提问来源于stack exchange,提问作者phildeg31
相关产品推荐
相关产品推荐

