37

I am using Retrofit 2 (2.0.0-beta3) with OkHttp client in Android application and so far everything going great. But currently I am facing issue with OkHttp Interceptor. The server I am communicating with is taking access token in body of request, so when I intercept the request to add auth token or in authenticate method of Authenticator when I need to add updated auth token, I need to modify body of request for this purpose. But it looks like I can only add data in headers but not in the body of ongoing request. The code I have written so far is as follows:

client.interceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                if (UserPreferences.ACCESS_TOKEN != null) {
                    // need to add this access token in request body as encoded form field instead of header
                    request = request.newBuilder()
                            .header("access_token", UserPreferences.ACCESS_TOKEN))
                            .method(request.method(), request.body())
                            .build();
                }
                Response response = chain.proceed(request);
                return response;
            }
        });

Can anyone point me to the right direction as how to modify request body to add my access token (first time or updated after token refresh)? Any pointer to right direction would be appreciated.

Lala Rukh
  • 373
  • 1
  • 3
  • 4

6 Answers6

47

I using this to add post parameter to the existing ones.

 OkHttpClient client = new OkHttpClient.Builder()
                    .protocols(protocols)
                    .addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Chain chain) throws IOException {
                            Request request = chain.request();
                            Request.Builder requestBuilder = request.newBuilder();
RequestBody formBody = new FormEncodingBuilder()
            .add("email", "Jurassic@Park.com")
            .add("tel", "90301171XX")
            .build();
                            String postBodyString = Utils.bodyToString(request.body());
                            postBodyString += ((postBodyString.length() > 0) ? "&" : "") +  Utils.bodyToString(formBody);
                            request = requestBuilder
                                    .post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"), postBodyString))
                                    .build();
                            return chain.proceed(request);
                        }
                    })
                    .build();

public static String bodyToString(final RequestBody request){
        try {
            final RequestBody copy = request;
            final Buffer buffer = new Buffer();
            if(copy != null)
                copy.writeTo(buffer);
            else
                return "";
            return buffer.readUtf8();
        }
        catch (final IOException e) {
            return "did not work";
        }
    }

OkHttp3:

RequestBody formBody = new FormBody.Builder()
                .add("email", "Jurassic@Park.com")
                .add("tel", "90301171XX")
                .build();
Fabian
  • 960
  • 13
  • 26
  • 1
    it would be a good idea to close the buffer in `bodyToString()` before returning – 3k- Nov 04 '16 at 21:08
  • @3k thats not needed, the Buffer doesnt allocate anything that can be closed inside the constructor. https://github.com/square/okio/blob/master/okio/src/main/java/okio/Buffer.java#L59 – Fabian Jan 24 '18 at 16:55
13

I'll share my Kotlin implementation of @Fabian's answer using Dagger. I wanted origin=app added to the request url for GET requests, and added to the body for form-encoded POST requests

@Provides
@Singleton
fun providesRequestInterceptor() =
        Interceptor {
            val request = it.request()

            it.proceed(when (request.method()) {
                "GET" -> {
                    val url = request.url()
                    request.newBuilder()
                            .url(url.newBuilder()
                                    .addQueryParameter("origin", "app")
                                    .build())
                            .build()
                }
                "POST" -> {
                    val body = request.body()
                    request.newBuilder()
                            .post(RequestBody.create(body?.contentType(),
                                    body.bodyToString() + "&origin=app"))
                            .build()
                }
                else -> request
            })
        }

fun RequestBody?.bodyToString(): String {
    if (this == null) return ""
    val buffer = okio.Buffer()
    writeTo(buffer)
    return buffer.readUtf8()
}
mgray88
  • 376
  • 1
  • 5
  • 10
9

Since this cannot be written in the comments of the previous answer by @Fabian, I am posting this one as separate answer. This answer deals with both "application/json" as well as form data.

import android.content.Context;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.Buffer;

/**
 * Created by debanjan on 16/4/17.
 */

public class TokenInterceptor implements Interceptor {
    private Context context; //This is here because I needed it for some other cause 

    //private static final String TOKEN_IDENTIFIER = "token_id";
    public TokenInterceptor(Context context) {
        this.context = context;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        RequestBody requestBody = request.body();
        String token = "toku";//whatever or however you get it.
        String subtype = requestBody.contentType().subtype();
        if(subtype.contains("json")){
            requestBody = processApplicationJsonRequestBody(requestBody, token);
        }
        else if(subtype.contains("form")){
            requestBody = processFormDataRequestBody(requestBody, token);
        }
        if(requestBody != null) {
            Request.Builder requestBuilder = request.newBuilder();
            request = requestBuilder
                    .post(requestBody)
                    .build();
        }

        return chain.proceed(request);
    }
    private String bodyToString(final RequestBody request){
        try {
            final RequestBody copy = request;
            final Buffer buffer = new Buffer();
            if(copy != null)
                copy.writeTo(buffer);
            else
                return "";
            return buffer.readUtf8();
        }
        catch (final IOException e) {
            return "did not work";
        }
    }
    private RequestBody processApplicationJsonRequestBody(RequestBody requestBody,String token){
        String customReq = bodyToString(requestBody);
        try {
            JSONObject obj = new JSONObject(customReq);
            obj.put("token", token);
            return RequestBody.create(requestBody.contentType(), obj.toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }
    private RequestBody processFormDataRequestBody(RequestBody requestBody, String token){
        RequestBody formBody = new FormBody.Builder()
                .add("token", token)
                .build();
        String postBodyString = bodyToString(requestBody);
        postBodyString += ((postBodyString.length() > 0) ? "&" : "") +  bodyToString(formBody);
        return RequestBody.create(requestBody.contentType(), postBodyString);
    }

}
Debanjan
  • 2,817
  • 2
  • 24
  • 43
3

You can edit the request body by below method, Pass the request and the parameter to edit.

private fun editBody(request: Request, parameter: String): RequestBody {
           
  val oldBody = request.body //retrieve the current request body
  val buffer =  Buffer()
  oldBody?.writeTo(buffer)

  val strOldBody = buffer.readUtf8() // String representation of the current request body
  buffer.clear()
  buffer.close()

  val strNewBody = JSONObject(strOldBody).put("parameter", parameter).toString()
  return strNewBody.toRequestBody(request.body?.contentType()) // New request body with the encrypted/modified string of the current request body

}

Now you can request again with updated request body

override fun intercept(chain: Interceptor.Chain): Response {
      val request: Request = chain.request()
      return chain.proceed(requestWithUpdatedParameter(request, "parameter"))
    
}
    
private fun requestWithUpdatedParameter(req: Request, parameter: String): Request {
                val newRequest: Request
                val body = editBody(req, parameter)
                newRequest = req.newBuilder().method(req.method, body).build()
                return newRequest
}
Shahab Rauf
  • 3,651
  • 29
  • 38
0
private static class NetworkInterceptor implements Interceptor {

@Override
public Response intercept(Chain chain) throws IOException {
    
    Request request = chain.request();
    RequestBody oldBody = request.body(); //retrieve the current request body
    Buffer buffer = new Buffer();
    oldBody.writeTo(buffer);
    String strOldBody = buffer.readUtf8(); // String representation of the current request body
    buffer.clear();
    buffer.close();

    MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
    String strNewBody = enDecService.encryptBody(strOldBody); // Your encryption/ modification logic 
    RequestBody body = RequestBody.create(mediaType, strNewBody); // New request body with the encrypted/modified string of the current request body

    request = request.newBuilder()
            .header("Content-Type", "application/json")
            .header("Content-Length", String.valueOf(body.contentLength()))
            .header("Authorization", "Bearer " + "your token")
            .method(request.method(), body).build();


    long t1 = System.nanoTime();
    Log.d(TAG, String.format("Sending request %s on %s", request.url(), request.headers()));

    Response response = chain.proceed(request); // sending req. to server. current req. body is a encrypted string.
    int maxAge = 6000; // read from cache for 6000 seconds even if there is internet connection
    response.header("Cache-Control", "public, max-age=" + maxAge);
    response = response.newBuilder().removeHeader("Pragma").build();


    long t2 = System.nanoTime();
    Log.d(TAG, String.format("Received response for %s in %.1fms  %s", response.request().url(), (t2 - t1) / 1e6d, response.toString()));

    try {
        String s = response.body().string(); // retrieve string representation of encrypted response assuming your response is encrypted.
        ResponseBody responseBody = ResponseBody.create(mediaType, enDecService.decryptBody(s)); // decrypt the encrypted response or make other modifications.yor decryption/modifications logic goes here.
        response = response.newBuilder().body(responseBody).build(); // build a new response with the decrypted response body.
    } catch (JOSEException e) {

    } catch (ParseException e) {

    }
    return response;
}

}

  • Could you add an explanation to your code? – Ailurophile May 15 '21 at 05:42
  • I have used an OkHttp Interceptor as u can see.Within the intercept(Chain chain) method you can get the original request body, but to manipulate it you have to convert it to a String object (using buffer) as Retrofit Request class doesn't provide any method for manipulation. You can then manipulate( like encrypt) the String object (by converting it into a model class using Serialization or Gson). Create a new Request object using the manipulated String object as the req. body of the new Request object then forward that request. Thank you. – sadaf fatin May 16 '21 at 16:20
-1

I'm using this way to verify my token

final OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS) //retrofit default 10 seconds
                .writeTimeout(30, TimeUnit.SECONDS) //retrofit default 10 seconds
                .readTimeout(30, TimeUnit.SECONDS) //retrofit default 10 seconds
                .addInterceptor(logging.setLevel(HttpLoggingInterceptor.Level.BODY))
                .addInterceptor(new BasicAuthInterceptor())
                .build();

Here i'm sending token through BasicAuthInterceptor

public class MyServiceInterceptor implements Interceptor {

private String HEADER_NAME="Authorization";
private String OBJECT_NAME="Bearer";
private String SPACE="  ";
@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    Request.Builder requestBuilder = request.newBuilder();

    String token= PreferenceManager.getInstance().getString(PreferenceManager.TOKEN);
        if (token != null) { {
            requestBuilder.addHeader(HEADER_NAME, OBJECT_NAME+SPACE+ token);
        }
}

    return chain.proceed(requestBuilder.build());

} }