Showing ProgressBar while uploading files in Android using OkHttp3 - android

I am trying to upload files in Amazon S3 using OkHttp3 library. I am able to upload the files using AsyncTask.Right now, I am showing a normal ProgressDialog. But, I want to replace the ProgressDialog with a ProgressBar. For this, I am doing the following things, but somehow it does not work:
1) Creating a custom ResponseBody and ProgressListener:
private static class ProgressResponseBody extends ResponseBody {
private final ResponseBody responseBody;
private final ProgressListener progressListener;
private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
this.responseBody = responseBody;
this.progressListener = progressListener;
}
#Override
public MediaType contentType() {
return responseBody.contentType();
}
#Override
public long contentLength() {
return responseBody.contentLength();
}
#Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
#Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
// read() returns the number of bytes read, or -1 if this source is exhausted.
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
return bytesRead;
}
};
}
}
interface ProgressListener {
void update(long bytesRead, long contentLength, boolean done);
}
2) Implementing the ProgressListener interface within the AsyncTask using for uploading the files:
final ProgressListener progressListener = new ProgressListener() {
#Override
public void update(long bytesRead, long contentLength, boolean done) {
Log.d(AppGlobal.TAG, " " + bytesRead);
Log.d(AppGlobal.TAG, " " + contentLength);
Log.d(AppGlobal.TAG, " " + done);
Log.d(AppGlobal.TAG, (100 * bytesRead) / contentLength + " % done ");
Log.d(AppGlobal.TAG, " " + "-------------------------");
}
};
3) Creating a OkHttp client and passing the progressListener created in Step 2 using a network interceptor:
Request request = new Request.Builder()
.header("Proxy-Authorization", "Basic " + Base64.encodeToString(data, Base64.NO_WRAP))
.addHeader("Content-Type", "application/octet-stream")
.addHeader("Connection", "Keep-Alive")
.url(url)
.put(RequestBody.create(MediaType.parse("application/octet-stream"), file))
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(1000, TimeUnit.SECONDS)
.writeTimeout(1000, TimeUnit.SECONDS)
.readTimeout(3000, TimeUnit.SECONDS)
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.addNetworkInterceptor(new Interceptor() {
#Override public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), progressListener))
.build();
}
})
.build();
okhttp3.Response response = client.newCall(request).execute();
But, the Log.d() statements used in the "update()" function of progressListener never gets executed.
If I can get these Log statements to print, then I can update the progress bar accordingly. Can someone kindly help me out.
Here is the full code used in "doInBackground" method of AsyncTask:
#Override
protected Void doInBackground(File... inputs) {
try {
file = inputs[0];
Date expiration = new Date();
long milliSeconds = expiration.getTime();
milliSeconds += 1000 * 60 * 60; // Add 1 hour.
expiration.setTime(milliSeconds);
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(Constants.BUCKET_NAME, AppGlobal.deviceId + "/" + file.getName());
generatePresignedUrlRequest.setMethod(HttpMethod.PUT);
generatePresignedUrlRequest.setContentType("application/octet-stream");
generatePresignedUrlRequest.setExpiration(expiration);
final URL url = Util.getS3Client(UploadActivity.this).generatePresignedUrl(generatePresignedUrlRequest);
Log.d("DXXXXXX", " URL -> " + url);
String domain = AppGlobal.getDomain(UploadActivity.this);
int port = Integer.valueOf(AppGlobal.getPort(UploadActivity.this));
final String signature = AppGlobal.getSignature(UploadActivity.this);
Proxy proxy = new Proxy(Proxy.Type.HTTP,
InetSocketAddress.createUnresolved(domain, port));//the proxy server(Can be your laptop ip or company proxy)
try {
String headerVal = String.format("%s:%s", "vzServices", signature);
final byte[] data = headerVal.getBytes("UTF-8");
okhttp3.Authenticator proxyAuthenticator = new okhttp3.Authenticator() {
#Override
public Request authenticate(Route route, okhttp3.Response response) throws IOException {
String credential = Credentials.basic("vzServices", signature);
return response.request().newBuilder().header("Proxy-Authorization", credential).build();
}
};
final ProgressListener progressListener = new ProgressListener() {
#Override
public void update(long bytesRead, long contentLength, boolean done) {
Log.d(AppGlobal.TAG, " " + bytesRead);
Log.d(AppGlobal.TAG, " " + contentLength);
Log.d(AppGlobal.TAG, " " + done);
Log.d(AppGlobal.TAG, (100 * bytesRead) / contentLength + " % done ");
Log.d(AppGlobal.TAG, " " + "-------------------------");
}
};
Request request = new Request.Builder()
.header("Proxy-Authorization", "Basic " + Base64.encodeToString(data, Base64.NO_WRAP))
.addHeader("Content-Type", "application/octet-stream")
.addHeader("Connection", "Keep-Alive")
.url(url)
.put(RequestBody.create(MediaType.parse("application/octet-stream"), file))
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(1000, TimeUnit.SECONDS)
.writeTimeout(1000, TimeUnit.SECONDS)
.readTimeout(3000, TimeUnit.SECONDS)
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.addNetworkInterceptor(new Interceptor() {
#Override public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), progressListener))
.build();
}
})
.build();
okhttp3.Response response = client.newCall(request).execute();
responseCode = response.code();
Log.d("Response code : ", " " + response.code());
Log.d("DXXXXXX", " URL 1 -> " + url);
success = true;
buffer = null;
} catch (Exception ex) {
// Handle the error
Log.d(TAG, "Error: " + ex.getLocalizedMessage());
ex.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

You should enable the progress bar first, just before calling the okhttp3 processing. Then, on response and on error, you should dismiss it.
Here's the snippet (I don't know okhttp but I've used volley many times. This example was kept simple enough to let you understand what I mean.)
sendJsonRequest(){
//enable progress bar here
enableProgressBar();
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, URL, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
// dismiss it
hideProgressDialog();
System.out.println(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// dismiss it and/or handle errors
hideProgressDialog();
// ...
}
});
queue.add(jsObjRequest);
}

Related

Get results of multiple network requests when they are all done

I'm running multiple network requests(getResponse method) in a for loop and I'm trying to get list of the responses only when ALL of the network requests are done.
I am trying to use CompletableFuture. getResponse uses OKHttp (asynch request and response)
Log.d("api_log", "Started doing things");
List<CompletableFuture> futures = new ArrayList();
for (int i = 0; i < mylist.size(); i++) {
try {
int finalI = i;
futures.add(CompletableFuture.runAsync(() -> getResponse(context, mylist.get(finalI).id)));
}
catch (Exception e) {}
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRunAsync(() -> Log.d("api_log", "Ended doing things"));
This is the getResponse method:
private void getResponse(final Context context, final String id) {
Log.d("api_log", "id is: " + id);
final String url = context.getString(R.string.myurl) + "/" + id;
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
Request request = new Request.Builder()
.url(url)
.method("GET", null)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
return;
}
// response HAS RECEIVED
final String strResponse = response.body().string();
Log.d("api_log", "response: " + strResponse);
}
});
}
Actual: "Ended doing things" is printed before all the responses are printed.
Expected: "Ended doing things" should be printed after all the responses are printed.
How can I achieve it?

Gson can not convert api response

I am just trying to show user data after hitting the API using Retrofit. my api response is:
{
"password":"111222333",
"name":"test name",
"email":"testem#gmail.com",
"username":"test1",
"customer_id":"201060",
"phone":"0196789"
}
but unfortunately, I am getting
"Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"
error.
I am totally stuck to show my json response.
My User.java class:
public class User {
#SerializedName("name")
#Expose
private String name;
#SerializedName("email")
#Expose
private String email;
#SerializedName("username")
#Expose
private String username;
#SerializedName("customer_id")
#Expose
private String customerId;
#SerializedName("phone")
#Expose
private String phone;
#SerializedName("password")
#Expose
private String password;
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getUsername() {
return username;
}
public String getCustomerId() {
return customerId;
}
public String getPhone() {
return phone;
}
public String getPassword() {
return password;
}
}
My Login class:
Gson gson = new GsonBuilder().setLenient().create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://us-central1-gmx-notification.cloudfunctions.net/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
all_api = retrofit.create(allApi.class);
private void getUserDetails(String userName,String passWord){
Call<User> call = all_api.getUserDetails(userName,passWord);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if(!response.isSuccessful()){
Log.d(response.body());
}
else{
User user = response.body();
String content = "";
content+= "Name: "+user.getName()+"\n";
content+= "Email: "+user.getEmail()+"\n";
content+= "Customer ID: "+user.getCustomerId()+"\n";
content+= "Phone: "+user.getPhone()+"\n";
Log.d(content);
}
});
}
and my retrofit api class:
package com.material.components;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface allApi {
#GET("login")
Call <User> getUserDetails(
#Query("email") String email,
#Query("password") String password
);
}
When i hit you api https://us-central1-gmx-notification.cloudfunctions.net/login?email=qwery#gmail.com&password=12345678
I got this response
Error: could not handle the request
So as your error says you expected Object but got a string. So or an error on the backend side or the request is incorrect or you forgot to add something to the request(Header or something else...).
For sure problem not in your model just got not a model that you expect in the response. Add Interceptor in your OkHttpClient to see what you get to be sure.
You need add this dependency to gradle
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
And here is a code example your API that will printing all networking stuff in the log:
public class NetworkManager {
private static RESTAuthService restAuthService;
/*timeout values in seconds*/
private static final int CONNECTION_TIMEOUT = 10;
private static final int WRITE_TIMEOUT = 10;
private static final int READ_TIMEOUT = 10;
static RESTAuthService getRESTAuthService() {
if (restAuthService == null) {
synchronized (NetworkManager.class) {
if (restAuthService == null) {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new RESTInterceptor())
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(NetworkConfig.BASE_AUTH_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
restAuthService = retrofit.create(RESTAuthService.class);
}
}
}
return restAuthService;
}
private static class RESTInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Buffer buffer = new Buffer();
if (request.body() != null) {
request.body().writeTo(buffer);
}
Log.d("HTTP Request", "Request to " + request.url().toString()
+ "\n" + request.headers().toString()
+ "\n" + buffer.readUtf8());
long t1 = System.nanoTime();
Response response = chain.proceed(request);
long t2 = System.nanoTime();
String msg = response.body().string();
msg = msg.replace("\r", ""); // Note: Messages with '\r' not displayed correctly in logcat
Log.d("HTTP Response", String.format("Response from %s in %.1fms%n\n%s",
response.request().url().toString(), (t2 - t1) / 1e6d, msg));
Log.d("HTTP Response", "Response code = " + response.code());
return response.newBuilder()
.body(ResponseBody.create(response.body().contentType(), msg))
.build();
}
}
}
Your MyLogin class will be something like this:
public class MuLogin {
/*timeout values in seconds*/
private static final int CONNECTION_TIMEOUT = 10;
private static final int WRITE_TIMEOUT = 10;
private static final int READ_TIMEOUT = 10;
allApi = all_api;
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new RESTInterceptor())
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://us-central1-gmx-notification.cloudfunctions.net/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
all_api =retrofit.create(allApi.class);
public void getUserDetails(String userName, String passWord) {
Call<User> call = all_api.getUserDetails(userName, passWord);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if (!response.isSuccessful()) {
Log.d(response.body());
} else {
User user = response.body();
String content = "";
content += "Name: " + user.getName() + "\n";
content += "Email: " + user.getEmail() + "\n";
content += "Customer ID: " + user.getCustomerId() + "\n";
content += "Phone: " + user.getPhone() + "\n";
Log.d(content);
}
});
}
}
private static class RESTInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Buffer buffer = new Buffer();
if (request.body() != null) {
request.body().writeTo(buffer);
}
Log.d("HTTP Request", "Request to " + request.url().toString()
+ "\n" + request.headers().toString()
+ "\n" + buffer.readUtf8());
long t1 = System.nanoTime();
Response response = chain.proceed(request);
long t2 = System.nanoTime();
String msg = response.body().string();
msg = msg.replace("\r", ""); // Note: Messages with '\r' not displayed correctly in logcat
Log.d("HTTP Response", String.format("Response from %s in %.1fms%n\n%s",
response.request().url().toString(), (t2 - t1) / 1e6d, msg));
Log.d("HTTP Response", "Response code = " + response.code());
return response.newBuilder()
.body(ResponseBody.create(response.body().contentType(), msg))
.build();
}
}
}

Android: OkHttp request within AsyncTask is called twice

I have a OkHttp request within an async taks doInBackgroun(), The resquest is a bit heavy and takes some time on my backend. Unfortunatly it looks like when OKHttp doesn't get an answer straight away it tries again, this makes my server blow up !
I have tried to disable this function but it seems to ignore it... What could i do ?
public class AsyncUpdateNewPatients extends AsyncTask<Object, Void, Boolean> {
private static OkHttpClient okHttpClient;
private static DatabaseHandler db;
ActivityMain activityMain;
public AsyncUpdateNewPatients (ActivityMain atv)
{
this.activityMain = atv;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
#Override
public void log(String message) {
Stormpath.logger().d(message);
}
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(httpLoggingInterceptor)
.retryOnConnectionFailure(false)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15L, TimeUnit.SECONDS)
.writeTimeout(15L, TimeUnit.SECONDS)
.build();
db = new DatabaseHandler(activityMain);
}
#Override
protected Boolean doInBackground(Object... objects) {
List<PatientData> allNewPatients = db.getAllNewPatients();
JSONArray allNewPatientJSONArray = new JSONArray();
for (PatientData tempPatientObject : allNewPatients) {
JSONObject tempPatientJSON = new JSONObject();
try {
tempPatientJSON.put("firstName", tempPatientObject.getFirstName());
tempPatientJSON.put("lastName", tempPatientObject.getLastName());
tempPatientJSON.put("height", tempPatientObject.getHeight());
tempPatientJSON.put("weight", tempPatientObject.getWeight());
tempPatientJSON.put("vaccines", tempPatientObject.getVaccinHistory());
tempPatientJSON.put("address", tempPatientObject.getAddress());
tempPatientJSON.put("zone", tempPatientObject.getZone());
tempPatientJSON.put("id", tempPatientObject.getId());
String dateOfBirth = tempPatientObject.getDateOfBirth().get(Calendar.DAY_OF_MONTH) + "/" + tempPatientObject.getDateOfBirth().get(Calendar.MONTH) + "/" + tempPatientObject.getDateOfBirth().get(Calendar.YEAR);
tempPatientJSON.put("dob",dateOfBirth);
} catch (JSONException e) {
e.printStackTrace();
}
allNewPatientJSONArray.put(tempPatientJSON);
}
if(allNewPatients.size() > 0){
JSONObject bodyJSON = new JSONObject();
try {
bodyJSON.put("allNewPatients", allNewPatientJSONArray);
} catch (JSONException e) {
e.printStackTrace();
}
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
final RequestBody body = RequestBody.create(JSON, String.valueOf(bodyJSON));
Request request = new Request.Builder()
.url(activityMain.getString(R.string.main_url) + "/api/syncFromOffLine")
.headers(buildStandardHeaders(Stormpath.accessToken()))
.post(body)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.d("DEBEUG", "error: " + e.getMessage());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if(response.code() == 200){
Log.d("DEBEUG", "response: " + response.body().string());
} else {
Log.d("DEBEUG", "there was an error: " + response.message().toString());
}
}
});
}
return true;
}

How to pass body to OKHttp message?

I have found this example below to send HTTP POST message with OKHttp.
I do not understand how to pass a body string to RequestBody. Why it takes two argument?
RequestBody formBody = new FormBody.Builder()
.add("message", "Your message")
.build();
Request request = new Request.Builder()
.url("http://rhcloud.com")
.post(formBody).addHeader("operation", "modifyRecords")
.build();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(responseBody.string());
}
}
});
}
}
I'm not sure if I understand what you mean quite right, but if you are asking why the FormBody .add() takes two arguments, it's because these are Key-Value-Pairs. The first parameter is the name and the second the value.
Anyway I think this example shows a clearer way how to post a string:
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}

OkHttp how to log request body

I'm using an interceptor, and I would like to log the body of a request I'm making but I can't see any way of doing this.
Is it possible ?
public class LoggingInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
Response response = chain.proceed(request);
long t2 = System.nanoTime();
double time = (t2 - t1) / 1e6d;
if (request.method().equals("GET")) {
Logs.info(String.format("GET " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), response.code(), response.headers(), response.body().charStream()));
} else if (request.method().equals("POST")) {
Logs.info(String.format("POST " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), request.body(), response.code(), response.headers(), response.body().charStream()));
} else if (request.method().equals("PUT")) {
Logs.info(String.format("PUT " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), request.body().toString(), response.code(), response.headers(), response.body().charStream()));
} else if (request.method().equals("DELETE")) {
Logs.info(String.format("DELETE " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITHOUT_BODY, request.url(), time, request.headers(), response.code(), response.headers()));
}
return response;
}
}
and the result :
POST [some url] in 88,7ms
ZoneName: touraine
Source: Android
body: retrofit.client.OkClient$1#1df53f05 <-request.body().toString() gives me this, but I would like the content string
Response: 500
Date: Tue, 24 Feb 2015 10:14:22 GMT
body: [some content]
Nikola's answer did not work for me. My guess is the implementation of ByteString#toString() changed. This solution worked for me:
private static String bodyToString(final Request request){
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
From the documentation of readUtf8():
Removes all bytes from this, decodes them as UTF-8, and returns the string.
which should be what you want.
I tried to comment on the correct answer from #msung, but my reputation isn't high enough.
Here's modification I did to print RequestBody before making it a full request. It works like a charm. Thanks
private static String bodyToString(final RequestBody request){
try {
final RequestBody copy = request;
final Buffer buffer = new Buffer();
copy.writeTo(buffer);
return buffer.readUtf8();
}
catch (final IOException e) {
return "did not work";
}
}
EDIT
Because I see there is still some people interested by this post, here is the final version (until next improvement) of my log interceptor. I hope it will save some of you guys's time.
Please note that this code is using OkHttp 2.2.0 (and Retrofit 1.9.0)
import com.squareup.okhttp.*;
import okio.Buffer;
import java.io.IOException;
public class LoggingInterceptor implements Interceptor {
private static final String F_BREAK = " %n";
private static final String F_URL = " %s";
private static final String F_TIME = " in %.1fms";
private static final String F_HEADERS = "%s";
private static final String F_RESPONSE = F_BREAK + "Response: %d";
private static final String F_BODY = "body: %s";
private static final String F_BREAKER = F_BREAK + "-------------------------------------------" + F_BREAK;
private static final String F_REQUEST_WITHOUT_BODY = F_URL + F_TIME + F_BREAK + F_HEADERS;
private static final String F_RESPONSE_WITHOUT_BODY = F_RESPONSE + F_BREAK + F_HEADERS + F_BREAKER;
private static final String F_REQUEST_WITH_BODY = F_URL + F_TIME + F_BREAK + F_HEADERS + F_BODY + F_BREAK;
private static final String F_RESPONSE_WITH_BODY = F_RESPONSE + F_BREAK + F_HEADERS + F_BODY + F_BREAK + F_BREAKER;
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
Response response = chain.proceed(request);
long t2 = System.nanoTime();
MediaType contentType = null;
String bodyString = null;
if (response.body() != null) {
contentType = response.body().contentType();
bodyString = response.body().string();
}
double time = (t2 - t1) / 1e6d;
if (request.method().equals("GET")) {
System.out.println(String.format("GET " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), response.code(), response.headers(), stringifyResponseBody(bodyString)));
} else if (request.method().equals("POST")) {
System.out.println(String.format("POST " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), stringifyRequestBody(request), response.code(), response.headers(), stringifyResponseBody(bodyString)));
} else if (request.method().equals("PUT")) {
System.out.println(String.format("PUT " + F_REQUEST_WITH_BODY + F_RESPONSE_WITH_BODY, request.url(), time, request.headers(), request.body().toString(), response.code(), response.headers(), stringifyResponseBody(bodyString)));
} else if (request.method().equals("DELETE")) {
System.out.println(String.format("DELETE " + F_REQUEST_WITHOUT_BODY + F_RESPONSE_WITHOUT_BODY, request.url(), time, request.headers(), response.code(), response.headers()));
}
if (response.body() != null) {
ResponseBody body = ResponseBody.create(contentType, bodyString);
return response.newBuilder().body(body).build();
} else {
return response;
}
}
private static String stringifyRequestBody(Request request) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
public String stringifyResponseBody(String responseBody) {
return responseBody;
}
}
With a current version of OkHttp, you can use the HTTP Logging Interceptor and set the level to BODY
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.BODY);
With this you cannot granularily configure the output for different HTTP methods, but it also works for other methods that might have a body.
Here an example showing the output of a PATCH request (minimally redacted):
--> PATCH https://hostname/api/something/123456 HTTP/1.1
Content-Type: application/json-patch+json; charset=utf-8
Content-Length: 49
Authorization: Basic YWRtaW46c2VjcmV0Cg==
Accept: application/json
[ { "op": "add", "path": "/path", "value": true }]
--> END PATCH (xx-byte body)
As you can see, this also prints out the headers and as the documentation states, you should really take care:
The logs generated by this interceptor when using the HEADERS or BODY levels have the potential to leak sensitive information such as "Authorization" or "Cookie" headers and the contents of request and response bodies. This data should only be logged in a controlled way or in a non-production environment.
You can redact headers that may contain sensitive information by calling redactHeader().
logging.redactHeader("Authorization");
logging.redactHeader("Cookie");
Version that handles requests with or without a body:
private String stringifyRequestBody(Request request) {
if (request.body() != null) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
Log.w(TAG, "Failed to stringify request body: " + e.getMessage());
}
}
return "";
}
Kotlin version :
val buf = okio.Buffer()
requestBody.writeTo(buf)
Log.d("AppXMLPostReq", "reqBody = ${buf.readUtf8()}")
Create a Separate new class and implement Intercepter.
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
var logInfo = ""
val requestBody=loggerUtil.getRequestBody
return response
}
yourOkHttpClient.addInterceptor(yourInstance)
GetRequestBody
var requestContent = ""
val requestBody = request.body
val buffer = Buffer()
if (requestBody != null) {
requestBody.writeTo(buffer)
}
val contentType = requestBody?.contentType()
val charset: Charset =
contentType?.charset(StandardCharsets.UTF_8) ?:StandardCharsets.UTF_8
if (buffer.isProbablyUtf8()) {
requestContent = buffer.readString(charset)
}
Extension to find Whether buffer data is UT8 format
fun Buffer.isProbablyUtf8(): Boolean {
try {
val prefix = Buffer()
val byteCount = size.coerceAtMost(64)
copyTo(prefix, 0, byteCount)
for (i in 0 until 16) {
if (prefix.exhausted()) {
break
}
val codePoint = prefix.readUtf8CodePoint()
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false
}
}
return true
} catch (_: EOFException) {
return false // Truncated UTF-8 sequence.
}
}
Are we supposed to call .close() on the buffer object ? or use the try-with-resources statement ?
private String getBodyAsString(Request request) throws IOException {
try(var buffer = new Buffer()) {
request.body().writeTo(buffer);
return buffer.readUtf8();
}
}

Categories

Resources