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

Retrofit拦截器中使用addHeader()无法生成正确请求的问题

Retrofit全局拦截器设置Content-Type不生效问题排查与解决

问题背景

尝试通过OkHttp的HeaderInterceptor全局添加Content-Type: application/json请求头,但测试发现不生效;在Retrofit接口方法上使用@Headers("Content-Type: application/json")注解则能正常请求服务器。排查后确认核心原因:

  • 拦截器方式发送请求时,最终传递的请求头为application/json; charset=UTF-8,服务器无法识别该格式
  • @Headers注解方式会保留原始指定的Header值,不会被自动追加编码后缀

问题根源

Retrofit的GsonConverterFactory在序列化请求体时,会自动为Content-Type追加; charset=UTF-8后缀。使用应用拦截器时,拦截器的执行顺序在Converter处理之前,所以拦截器设置的纯application/json会被Converter修改;而@Headers注解的Header会被Retrofit直接使用,不会被Converter修改。

解决方法

方法一:改用OkHttp网络拦截器

网络拦截器在请求体被Converter处理之后、实际发送给服务器之前执行,可直接覆盖被修改后的Content-Type:

// 修改OkHttpClient构建逻辑,使用addNetworkInterceptor
private val client = OkHttpClient.Builder()
    .addNetworkInterceptor(HeaderInterceptor())
    .build()

class HeaderInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
            .newBuilder()
            .header("Content-Type", "application/json") // 用header()覆盖原有值
            .build()
        return chain.proceed(request)
    }
}

方法二:自定义GsonConverterFactory,禁用自动追加charset

如果不想修改拦截器类型,可自定义Converter,避免自动添加编码后缀:

// 自定义ConverterFactory
class CustomGsonConverterFactory private constructor(private val gson: Gson) : Converter.Factory() {
    companion object {
        fun create(gson: Gson = Gson()): CustomGsonConverterFactory {
            return CustomGsonConverterFactory(gson)
        }
    }

    override fun requestBodyConverter(
        type: Type,
        parameterAnnotations: Array<Annotation>,
        methodAnnotations: Array<Annotation>,
        retrofit: Retrofit
    ): Converter<*, RequestBody>? {
        val adapter = gson.getAdapter(TypeToken.get(type))
        return CustomGsonRequestBodyConverter(gson, adapter)
    }
}

// 自定义RequestBodyConverter
class CustomGsonRequestBodyConverter<T>(
    private val gson: Gson,
    private val adapter: TypeAdapter<T>
) : Converter<T, RequestBody> {
    private val MEDIA_TYPE = MediaType.parse("application/json")

    override fun convert(value: T): RequestBody {
        val buffer = Buffer()
        val writer = OutputStreamWriter(buffer.outputStream(), Charset.forName("UTF-8"))
        val jsonWriter = gson.newJsonWriter(writer)
        adapter.write(jsonWriter, value)
        jsonWriter.close()
        return RequestBody.create(MEDIA_TYPE, buffer.readByteString())
    }
}

然后替换Retrofit中的ConverterFactory:

fun getRetrofit(): Retrofit{
    return Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(client)
        .addConverterFactory(CustomGsonConverterFactory.create()) // 使用自定义Converter
        .build()
}

注意事项

  • 使用header()方法而非addHeader(),确保覆盖所有已存在的Content-Type头,避免出现多个重复Header的情况
  • 两种方法都能确保最终发送给服务器的Content-Type为纯application/json,适配服务器的识别要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 13:11:36