紀錄開發上常遇到的問題,避免重複踩坑。
紀錄API請求和回應
可使用寫好的套件 HttpLoggingInterceptor,參考https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor
或自行實作。
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
| public class LogInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); String bodyString = bodyToString(request); Log.d("Mike", String.format("[%X] Request to %s\n%s", chain.hashCode(), request.url(), bodyString));
Response response; try { response = chain.proceed(request); } catch (SocketTimeoutException e) { Log.d("Mike", String.format("[%X] Response timeout for %s", chain.hashCode(), request.url())); throw e; } ResponseBody responseBody = response.body(); String responseBodyString = responseBody.string(); Log.d("Mike", String.format("[%X] Response to %s(%d)\n%s", chain.hashCode(), response.request().url(), response.code(), responseBodyString));
return response.newBuilder().body( ResponseBody.create(responseBody.contentType(), responseBodyString.getBytes())).build(); }
private String bodyToString(Request request) { try { Request copy = request.newBuilder().build(); Buffer buffer = new Buffer(); if (copy.body() != null) { copy.body().writeTo(buffer); return buffer.readUtf8(); } return ""; } catch (IOException e) { return ""; } } }
|
若要印出headers,可加上這段。
注意:不要使用response.headers().names()跑for each處理,例如回傳的Set-Cookie有兩個,但該寫法只會印出一個。
1 2 3 4
| Headers headers = response.headers(); for (int i = 0; i < headers.size(); i++) { Log.d("Mike", headers.name(i) + ": " + headers.value(i)); }
|
請求加上token認證
例如使用Google API需要獲得token後,才有權限使用。
1 2 3 4 5 6 7 8 9
| public class TokenInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request().newBuilder() .header("Authorization", "Bearer " + token) .build(); return chain.proceed(request); } }
|
保持連線和Cookie
參考https://stackoverflow.com/questions/36706795/how-to-keep-session-using-retrofit-okhttpclient
1
| implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.9.3'
|
1 2 3
| OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder() .cookieJar(new JavaNetCookieJar(new CookieManager())) .build();
|
修改FieldMap
使用情境是有多個API都要傳同個欄位和值,例如UUID或時間戳記。
使用下列方式可對所有API都加上該值;但若是遇到只有部分需要,則不適用。建議改用Builder類別包住Map操作,然後將常用的欄位寫成public的method。
參考https://stackoverflow.com/questions/56515769/okhttp3-interceptor-add-fields-to-request-body
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class FieldMapInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if ("POST".equals(request.method()) && request.body() instanceof FormBody) { FormBody.Builder bodyBuilder = new FormBody.Builder(); FormBody formBody = (FormBody) request.body(); for (int i = 0; i < formBody.size(); i++) { bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i)); } bodyBuilder.add("key", "value"); request = request.newBuilder().post(bodyBuilder.build()).build(); } return chain.proceed(request); } }
|
上送檔案(body為raw)
以Google Photos API的uploads為例,參考 [Android] Retrofit應用 - Google Photos API
上送檔案(body為form-data)
以工作上遇到的日誌上送為例。
1 2 3 4
| public interface CustomAPI { @POST("Upload") Call<ResponseBody> uploadLog(@Body RequestBody body); }
|
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
| public void uploadLog(String date, File zipFile) { Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://xxx/") .addConverterFactory(GsonConverterFactory.create()) .build();
final RequestBody body = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("date", date) .addFormDataPart("log", zipFile.getName(), RequestBody.create(null, zipFile)) .build();
retrofit.create(CustomAPI.class).uploadLog(body) .enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { }
@Override public void onFailure(Call<ResponseBody> call, Throwable t) {
} }); }
|
以Google Photos API的mediaItems:batchCreate為例。因其使用gRPC轉碼語法,若直接使用會有問題,endpoint開頭加上./即可。
1 2 3 4
| public interface PhotoAPI { @POST("./mediaItems:batchCreate") Call<BatchCreateMediaItemsResponse> batchCreateMediaItems(@Body BatchCreateMediaItemsRequest request); }
|
參考資料
- https://developers.google.com/photos/library/reference/rest/v1/mediaItems/batchCreate?hl=zh-tw
- https://stackoverflow.com/questions/54406788/retrofit-how-to-use-colon-in-url