關於Retrofit2+Okhttp3實現統一新增請求引數和重定向

LeBron_Six發表於2016-11-08

Android開發中難免會遇到一些比較“不友好”的服務端介面。比如以前遇到的json資料中,某個欄位偶爾為Object,偶爾為List…

最近遇到的一個問題就是:所有請求介面都要增加一個token引數… 並且token引數有可能過期,比如請求某一條介面,如果token失效則在該請求的響應中把新的token帶回來,客戶端就得用新的token再次傳送該請求,類似重定向。

token失效的stateCode為3,新的token在data欄位中返回。如下:

{
    "stateCode":3,
    "data":"E78kH6",
    "errorMsg":null
}

慶幸的是,Okhttp提供了強大的攔截器功能,是一種能夠監控,重寫,重試呼叫的強大機制。

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。

Request request = oldRequest.newBuilder()
        .header("Content-Encoding", "gzip")
        .build();

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

然後為OkHttp配置一個攔截器。

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

Retrofit retrofit = new Retrofit.Builder()
    .addConverterFactory(ScalarsConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl(baseUrl)
    .client(getOkHttpClient())
    .build();

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

相關文章