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

如何解决Spring Boot RestTemplate设置Accept请求头无效的问题?

解决RestTemplate无法覆盖Accept请求头的问题

我完全懂你此刻的困扰——明明手动把Accept头设成了application/json,但RestTemplate死活要带上text/plain这类额外类型,导致GraphQL服务返回406错误。这背后的核心原因和RestTemplate的消息转换器(MessageConverter)机制直接相关:

当你调用exchange方法并指定响应类型为String.class时,RestTemplate会自动遍历它注册的所有HttpMessageConverter,把这些转换器支持的MediaType全部追加到Accept头里。默认情况下,StringHttpMessageConverter是最先被注册的,它默认支持text/plain,所以不管你手动设置了什么Accept值,这个类型都会被强制加上。

下面是几个可行的解决方案,按推荐程度排序:

方案1:修改StringHttpMessageConverter的支持类型

你可以自定义RestTemplate,调整StringHttpMessageConverter的支持MediaType,只保留application/json,这样就不会自动添加text/plain了:

RestTemplate restTemplate = new RestTemplate();
// 找到StringHttpMessageConverter并修改其支持的MediaType
for (HttpMessageConverter<?> converter : restTemplate.getMessageConverters()) {
    if (converter instanceof StringHttpMessageConverter) {
        ((StringHttpMessageConverter) converter).setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));
        break;
    }
}

// 后续请求代码和你原来的逻辑一致
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/graphql");
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
String content = "{products(query: \"title:tow*\", first: 10) {edges {node {id title}}}}";
HttpEntity<String> requestEntity = new HttpEntity<>(content, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

方案2:添加请求拦截器强制覆盖Accept头

通过ClientHttpRequestInterceptor可以在请求发送前强制重置Accept头,彻底覆盖所有自动添加的类型:

RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add((request, body, execution) -> {
    // 强制将Accept头设置为application/json,覆盖所有默认值
    request.getHeaders().setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    return execution.execute(request, body);
});

// 原来的请求代码无需大幅修改
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/graphql");
String content = "{products(query: \"title:tow*\", first: 10) {edges {node {id title}}}}";
HttpEntity<String> requestEntity = new HttpEntity<>(content, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

方案3:移除不需要的MessageConverter

如果你的场景完全不需要StringHttpMessageConverter,可以直接移除它,这样RestTemplate就不会自动添加text/plain到Accept头了:

RestTemplate restTemplate = new RestTemplate();
// 移除StringHttpMessageConverter
restTemplate.getMessageConverters().removeIf(converter -> converter instanceof StringHttpMessageConverter);

// 设置请求头并发送请求
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/graphql");
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
String content = "{products(query: \"title:tow*\", first: 10) {edges {node {id title}}}}";
HttpEntity<String> requestEntity = new HttpEntity<>(content, headers);
// 注意:如果用String.class可能会报错,可改用byte[]或自定义适配的转换器
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

为什么手动设置setAccept不生效?

因为RestTemplate在准备请求时,会根据你指定的响应类型(这里是String.class),从MessageConverter中收集所有支持的MediaType,然后合并到你手动设置的Accept头里——你的手动设置是被追加,而不是覆盖自动添加的类型。这就是日志里会出现[text/plain, application/json, application/*+json, */*]的根本原因。

内容的提问来源于stack exchange,提问作者rram

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 15:18:17