Android中Retrofit+OkHttp@Body请求自动添加chunked头问题求助
我懂你的困扰——服务器不支持chunked编码,用@Body RequestBody发起请求时OkHttp自动加了这个头,写了Interceptor移除却完全没用,又不想把所有对象提前手动转成字符串。咱们来一步步搞定这个问题:
为什么移除Interceptor无效?
核心原因很关键:Transfer-Encoding: chunked这个头不是在初始请求里就存在的,而是OkHttp在准备发送请求时,检测到请求体的contentLength()方法返回-1(也就是无法提前确定请求体长度)时自动添加的。你在Interceptor里移除的是初始请求的头,但这个头是后续OkHttp自动补上的,所以完全起不到作用。
可行解决方案
方案1:让RequestBody返回准确的Content-Length
OkHttp只有在拿不到请求体长度时才会触发chunked编码,所以只要让你的RequestBody能返回精准的内容长度,就能避免这个问题。
如果是用Gson将对象转成JSON请求体,你可以自定义一个RequestBody,提前计算好JSON字符串的长度,示例代码如下:
public class JsonRequestBody extends RequestBody { private final MediaType mediaType; private final String jsonContent; public JsonRequestBody(Object targetObj, MediaType mediaType) { this.mediaType = mediaType; this.jsonContent = new Gson().toJson(targetObj); } @Override public MediaType contentType() { return mediaType; } @Override public long contentLength() throws IOException { // 返回JSON字符串的UTF-8字节长度 return jsonContent.getBytes(StandardCharsets.UTF_8).length; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.writeString(jsonContent, StandardCharsets.UTF_8); } }
使用的时候替换原来的RequestBody.create():
// 传入你的业务对象和媒体类型 RequestBody payload = new JsonRequestBody(yourMemberObject, MediaType.parse("application/json; charset=utf-8"));
这样OkHttp能拿到准确的contentLength,就不会再自动添加Transfer-Encoding: chunked头了。
方案2:直接用Retrofit的Converter传对象(最省心)
其实你根本不用手动构建RequestBody——Retrofit的GsonConverterFactory(或其他JSON转换器)可以自动帮你把Java对象序列化为JSON请求体,并且自动计算contentLength,从根源上避免chunked编码。
步骤如下:
- 确保你的Retrofit实例已经添加了Gson转换器:
Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(httpClient.build()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 如果你用RxJava .build();
- 修改接口方法,直接传业务对象作为
@Body参数:
@POST("members") Observable<Response<Void>> createMember( @Header(X_CLIENT_AUTHORIZATION) String clientAuthorization, @Body MemberModel payload // 直接传入你的Java对象即可 );
这种方式完全符合Retrofit的设计理念,不用手动处理序列化和长度计算,还能避免chunked问题,绝对是首选方案。
方案3:强制设置Content-Length(应急备选,不推荐)
如果上面的方案都无法适配你的场景,可以尝试通过Interceptor强制设置Content-Length(前提是能拿到请求体长度),但这种方式比较hack,可能随OkHttp版本更新失效:
httpClient.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); if (originalRequest.body() != null) { try { // 通过反射获取请求体的contentLength Method lengthMethod = originalRequest.body().getClass().getMethod("contentLength"); lengthMethod.setAccessible(true); long contentLength = (long) lengthMethod.invoke(originalRequest.body()); // 手动设置Content-Length头,OkHttp就不会加chunked了 Request modifiedRequest = originalRequest.newBuilder() .header("Content-Length", String.valueOf(contentLength)) .build(); return chain.proceed(modifiedRequest); } catch (Exception e) { e.printStackTrace(); } } return chain.proceed(originalRequest); } });
验证方法
可以添加OkHttp的HttpLoggingInterceptor打印完整请求头,确认Transfer-Encoding: chunked是否已经消失:
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); httpClient.addInterceptor(loggingInterceptor);
内容的提问来源于stack exchange,提问作者Rybzor

