You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring同URL不同Produces的POST方法返回406,是否支持该配置?

Spring支持同URL同POST方法的内容协商吗?

当然支持!你完全不需要把第二个方法改成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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:20:35