最近将项目中的Retrofit由1.9升级到2.3版本。
在没有更改请求配置的情况下,发现请求头的Content-Type不一样了。

通过抓包查看请求,
1.9版本POST请求的Header字段

Content-Type:application/x-www-form-urlencoded; charset=utf-8

2.3版本POST请求Header字段

Content-Type:application/x-www-form-urlencoded

先说解决办法:
在Interface声明中添加注解

@Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")

例如原有代码

@FormUrlEncoded
@POST("mobileapi")
Observable<Result<YourData>> queryData(@Field("pageNo") Integer num);

变为

@FormUrlEncoded
@POST("mobileapi")
@Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Observable<Result<YourData>> queryData(@Field("pageNo") Integer num);

原因:
对于此处的POST请求,在Retrofit1.9中通过FormUrlEncodedTypedOutput类获取Content-Type

@Override public String mimeType() {
  return "application/x-www-form-urlencoded; charset=UTF-8";
}

而Retrofit2.3中,通过FormBody类获取Content-Type

private static final MediaType CONTENT_TYPE =
    MediaType.parse("application/x-www-form-urlencoded");

因此原有代码的情况下,1.9获取的默认值带有charset,2.3版本则没有。

为何在添加注解后就可以改变Header字段了呢?
在Retrofit2生成调用时:
1. 通过ServiceMethod类的parseParameterAnnotation方法处理接口注解
2. 当发现注解声明了Content-Type,会读取并存储其字段
3. 建立请求时,通过RequestBuilder类进行,将参数传递给Builder生成request
4. requestBuilder根据FormUrlEncode注解,会默认生成FormBody类型的body
5. requestBuilder判断第一步得到的content-type是否为空,此时不为空,则新建ContentTypeOverridingRequestBody覆盖body变量
6. 发起请求时,进入okhttp3的RealCall类处理
7. 调用getResponseWithInterceptionChain,按顺序添加用户定义的拦截器,以及okhttp内部拦截器
8. 内部拦截器BridgeInterceptor执行,调用body变量读取Content-Type
9. 根据第5步处理,由于接口声明了Content-Type,这里获取到的是ContentTypeOverridingRequestBody类型的body,返回了用户指定的字段,因此加上了charset

更多推荐

升级Retrofit2遇到的POST请求中Content-Type字段问题