Spring同URL不同Produces的POST方法返回406,是否支持该配置?
当然支持!你完全不需要把第二个方法改成GET请求——Spring的内容协商机制本来就支持根据Accept请求头来匹配同一个URL、同一个HTTP方法下的不同处理器方法。出现406错误的原因大概率是缺少text/csv对应的消息转换器,或者返回值的处理方式不对,下面给你详细分析和解决办法:
为什么会出现406错误?
406(Not Acceptable)意味着Spring找到了匹配URL和POST方法的控制器方法,但无法将方法的返回值转换成请求Accept头要求的text/csv格式。这是因为Spring默认并没有注册处理text/csv类型的消息转换器,所以即使你的方法指定了produces = TEXT_CSV,Spring也不知道该怎么把Resource转换成CSV格式的响应体。
解决办法
你有两种常见的方式来解决这个问题:
方式一:自定义CSV消息转换器
创建一个自定义的HttpMessageConverter,让Spring知道如何处理text/csv类型的响应。比如下面这个示例(假设你的Resource指向一个已生成的CSV文件):
@Component public class CsvResourceMessageConverter extends AbstractHttpMessageConverter<Resource> { private static final MediaType CSV_MEDIA_TYPE = MediaType.parseMediaType("text/csv"); public CsvResourceMessageConverter() { super(CSV_MEDIA_TYPE); } @Override protected boolean supports(Class<?> clazz) { // 只处理Resource类型的返回值 return Resource.class.isAssignableFrom(clazz); } @Override protected Resource readInternal(Class<? extends Resource> clazz, HttpInputMessage inputMessage) throws IOException { // 我们的接口是POST请求,不需要接收CSV请求体,所以这里直接抛出不支持的异常 throw new HttpMessageNotSupportedException("Reading CSV request bodies is not supported by this endpoint"); } @Override protected void writeInternal(Resource resource, HttpOutputMessage outputMessage) throws IOException { // 将Resource中的CSV内容写入响应输出流 try (InputStream in = resource.getInputStream(); OutputStream out = outputMessage.getBody()) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } } }
把这个转换器注册到Spring容器后,当请求携带Accept: text/csv时,Spring就能正确调用你的exportToCsv方法,并将Resource转换成CSV响应。
方式二:手动处理响应(无需自定义转换器)
如果你不想自定义转换器,可以直接在方法里手动构建响应,设置好响应头和CSV内容的字节数组:
@PostMapping(value = "/boat/v1/private-owners/search", produces = TEXT_CSV) public ResponseEntity<byte[]> exportToCsv(@Valid @RequestBody SearchOwnersRequest request, Pageable pageable) throws IOException { // 1. 调用业务逻辑获取数据 OwnerViewResponse response = privateOwnerFacade.findOwners(request, pageable); // 2. 将数据转换成CSV格式的字节数组(这里需要你自己实现CSV生成逻辑,比如用OpenCSV等库) byte[] csvBytes = generateCsvFromOwnerResponse(response); // 3. 设置响应头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType(TEXT_CSV)); // 如果需要让浏览器下载文件,添加Content-Disposition头 headers.setContentDisposition(ContentDisposition.attachment() .filename("private-owners.csv") .build()); // 4. 返回响应 return new ResponseEntity<>(csvBytes, headers, HttpStatus.OK); }
这种方式更直接,不需要依赖额外的消息转换器,Spring会直接把字节数组作为响应体返回,同时带上你设置的text/csv Content-Type,完美匹配Accept头的要求。
额外检查点
- 确认你的
Accept请求头没有拼写错误,比如是不是写成了text/csv;charset=utf-8(这种情况Spring也能匹配,但如果权重设置不对可能有问题,不过你说的是直接Accept: text/csv,应该没问题)。 - 确保
exportToCsv方法里生成的CSV内容是有效的,没有IO异常导致无法读取Resource。
内容的提问来源于stack exchange,提问作者Andrey Yaskulsky

