关于Retrofit2+Okhttp3实现统一添加请求参数和重定向

Android开发中难免会遇到一些比较“不友好”的服务端接口。比如以前遇到的json数据中,某个字段偶尔为Object,偶尔为List…

最近遇到的一个问题就是:所有请求接口都要增加一个token参数… 并且token参数有可能过期,比如请求某一条接口,如果token失效则在该请求的响应中把新的token带回来,客户端就得用新的token再次发送该请求,类似重定向。

token失效的stateCode为3,新的token在data字段中返回。如下:

1
2
3
4
5
{
"stateCode":3,
"data":"E78kH6",
"errorMsg":null
}

庆幸的是,Okhttp提供了强大的拦截器功能,是一种能够监控,重写,重试调用的强大机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class TokenInterceptord implements Interceptor {

private final String TAG = "respond";

@Override
public Response intercept(Chain chain) throws IOException {
Request oldRequest = chain.request();
String url = oldRequest.url().toString();
Response response = null;

// 新的请求,添加参数
Request newRequest = addParam(oldRequest);
response = chain.proceed(newRequest);

ResponseBody value = response.body();
byte[] resp = value.bytes();
String json = new String(resp, "UTF-8");

// 判断stateCode值
try {
JSONObject jsonObject = new JSONObject(json);
int stateCode = jsonObject.optInt("stateCode");
if (stateCode == 3) {
String data = jsonObject.optString("data");
Log.d(TAG, "token失效,新的token:" + data);
DataStorageUtils.saveToken(data);
// token失效,重新执行请求
Request newTokenRequest = addParam(oldRequest);
response = chain.proceed(newTokenRequest);
} else {
// 这里值得注意。由于前面value.bytes()把响应流读完并关闭了,所以这里需要重新生成一个response,否则数据就无法正常解析了
response = response.newBuilder()
.body(ResponseBody.create(null, resp))
.build();
}
} catch (Exception e) {

}

return response;
}

/**
* 添加公共参数
*
* @param oldRequest
* @return
*/
private Request addParam(Request oldRequest) {

HttpUrl.Builder builder = oldRequest.url()
.newBuilder()
.setEncodedQueryParameter("lversion", PackagesUtils.getAppVersionName())
.setEncodedQueryParameter("token", DataStorageUtils.getToken());

Request newRequest = oldRequest.newBuilder()
.method(oldRequest.method(), oldRequest.body())
.url(builder.build())
.build();

return newRequest;
}
}

当然,也可以为请求或响应添加Header。

1
2
3
4
5
6
7
Request request = oldRequest.newBuilder()
.header("Content-Encoding", "gzip")
.build();

Response response = response.newBuilder()
.header("Content-Encoding", "gzip")
.build();

然后为OkHttp配置一个拦截器。

1
2
3
4
5
6
7
8
9
10
public static OkHttpClient getOkHttpClient() {
Interceptor interceptor = new TokenInterceptor();
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
.addInterceptor(interceptor)
.addInterceptor(new HttpLoggingInterceptor(new MyLog()).setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
return client;
}

Retrofit 使用 Okhttp作为client

1
2
3
4
5
6
7
8
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseUrl)
.client(getOkHttpClient())
.build();

Api api = retrofit.create(Api.class);