Spring Boot中如何修改Feign响应头 将text/plain改为application/json
问题原因
你之前配置的@Headers注解、HeaderTestRequestInterceptor仅作用于发往目标服务的请求,无法修改服务端返回的响应头。默认配置的JacksonDecoder只会识别Content-Type为application/json的响应,遇到响应头为text/plain但实际内容为JSON的返回值时,不会执行JSON反序列化逻辑,因此无法转换为POJO对象。
解决方案
以下两个方案可直接落地,按需选择即可。
方案1:自定义Decoder兼容text/plain类型(兼容性最好,推荐)
无需单独修改响应头,直接扩展JacksonDecoder,让其支持解析text/plain类型的JSON内容,代码如下:
import com.fasterxml.jackson.databind.ObjectMapper; import feign.FeignException; import feign.Response; import feign.jackson.JacksonDecoder; import java.io.IOException; import java.lang.reflect.Type; import java.util.Collections; import java.util.regex.Pattern; public class CustomJacksonDecoder extends JacksonDecoder { private static final Pattern TEXT_CONTENT_PATTERN = Pattern.compile("text/.*"); public CustomJacksonDecoder() { super(); } public CustomJacksonDecoder(ObjectMapper objectMapper) { super(objectMapper); } @Override public Object decode(Response response, Type type) throws IOException, FeignException { // 检测到text类型响应时,替换Content-Type头为application/json后再走原生解析逻辑 if (response.headers().getOrDefault("Content-Type", Collections.emptyList()) .stream().anyMatch(contentType -> TEXT_CONTENT_PATTERN.matcher(contentType).matches())) { response = response.toBuilder() .replaceHeader("Content-Type", "application/json;charset=UTF-8") .build(); } return super.decode(response, type); } }
替换Feign构建逻辑中的Decoder配置即可生效:
this.feign = Feign.builder() .requestInterceptor(new HeaderTestRequestInterceptor()) .encoder(new JacksonEncoder()) // 替换为自定义Decoder .decoder(new CustomJacksonDecoder()) .contract(new SpringMvcContract()) // 注意:这里第一个参数必须替换为你自定义的Feign接口类型 .target(MozambiqueheApi.class, "https://api.test.re");
方案2:通过响应拦截器统一修改Content-Type头
该方案适用于Feign 11及以上版本,低版本请使用方案1。
如果你需要在解码环节前统一修改所有响应的Content-Type,可以自定义响应拦截器:
import feign.InvocationContext; import feign.Response; import feign.ResponseInterceptor; import java.util.Collections; public class ContentTypeModifyInterceptor implements ResponseInterceptor { @Override public Object intercept(InvocationContext context, Chain chain) throws Exception { Response response = context.response(); if (response != null) { boolean isTextPlain = response.headers().getOrDefault("Content-Type", Collections.emptyList()) .stream().anyMatch(ct -> ct.contains("text/plain")); if (isTextPlain) { Response modifiedResponse = response.toBuilder() .replaceHeader("Content-Type", "application/json;charset=UTF-8") .build(); context = context.toBuilder().response(modifiedResponse).build(); } } return chain.next(context); } }
构建Feign客户端时注册该拦截器即可:
this.feign = Feign.builder() .requestInterceptor(new HeaderTestRequestInterceptor()) // 注册响应拦截器 .responseInterceptor(new ContentTypeModifyInterceptor()) .encoder(new JacksonEncoder()) .decoder(new JacksonDecoder()) .contract(new SpringMvcContract()) .target(MozambiqueheApi.class, "https://api.test.re");
额外注意点
- 你在Feign接口方法上加的
@Headers("Content-Type: application/json")属于冗余配置:GET请求无请求体,不需要指定该请求头,且你已经在请求拦截器中做了同名配置。 - 原有代码中Feign构建逻辑的
.target()方法第一个参数传入Feign.class是错误的,必须传入你自定义的接口类型MozambiqueheApi.class,否则接口方法无法完成映射,调用时会抛出方法不存在的异常。 - 你当前定义的
TestPojo类仅映射了global.name字段,接口返回的uid、level、bans等字段如果需要使用,自行在实体类中补充对应属性和get/set方法即可。
内容的提问来源于stack exchange,提问作者Soul
相关产品推荐
相关产品推荐

