I am using Retrofit in an Android application. When I hit an API with token to get user information it gives cached(previous) response. Whenever I logged out and log in again API gives previous user detail, I tested API in Postman it works fine there.
I have tried some solutions I searched but nothing is working.
Response header that I am getting is
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Kestrel
Date: Mon, 08 Jan 2018 09:35:26 GMT
Below is ApiClient class
public class ApiClient {
public static final String BASE_URL = "http://XX.XXX.XXX.XX/api/";
private static Retrofit authRetrofit = null;
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
}
return retrofit;
}
public static Retrofit getAuthorizeClient(final String token) {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.addHeader("Authorization", "Bearer " + token)
.addHeader("Cache-control", "no-cache")
.method(original.method(), original.body())
//.cacheControl(CacheControl.FORCE_NETWORK)
.build();
return chain.proceed(request);
}
});
OkHttpClient client = httpClient.cache(null).build();
if (authRetrofit == null) {
authRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.client(client).build();
}
return authRetrofit;
}
}
In your httpClient interceptor, try adding cache control header:
.addHeader("Cache-control", "no-cache");
EDIT
Just noticed you are on Retrofit2
original.newBuilder().header("Cache-control", "no-cache");
Alternatively try adding it as an annotation to your API method:
#Headers("Cache-control: no-cache")
Response callApi();
According to Docs.
EDIT 2
Okay I suspect it's because of your if condition, your authRetrofit wouldn't be updated if condition failed. Try removing it
if (authRetrofit == null)
Help this helps.
Use Post request instead, I've faced the same problem, rectified by changing my GET method to post and send one random string as a Field.
Related
I've tried sending the token with a HeaderMap but get a 401 code response. The way my project is setup is that I have a separate file for my ApiClient and I have a OkHttpClient Interceptor and a HttpLoggingInterceptor to see whats going on, however I can't get the Bearer Token to work. I've seen solutions that add it to the interceptor as a header in the interceptor and I've tried this but since my token is saved in SharedPreferences I can't get it to work in the ApiClient class I have.
This is the ApiClient
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Gson gson = new GsonBuilder().serializeNulls().setLenient().create();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#NotNull
#Override
public okhttp3.Response intercept(#NotNull Chain chain) throws IOException {
Request originalRequest = chain.request();
Request newRequest = originalRequest.newBuilder()
//I would add the header here
//I tried this but it says on "ApiClient.this" cannot be referenced from static context
// .header("Authorization" , SharedPreferencesHelper.getUserToken(ApiClient.this));
.build();
return chain.proceed(newRequest);
}
})
.addInterceptor(interceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.0.6:8000/api/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
This is the method from SharedPreferencesHelper.getUserToken(MainActivity.this)
public static String getUserToken(Context context) {
SharedPreferences sp = getSharedPreferences(context);
return sp.getString(USER_TOKEN, null);
}
This is the current call where the response is 401, If I don't add the Accept => application/json the response url is incorrect and also returns a html page when I need a simple response return response("LoggedOut", 200); //this is the response in the api
Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json");
headers.put("Token", SharedPreferencesHelper.getUserToken(MainActivity.this));
Call<Void> call = apiInterface.LogoutUser(headers);
call.enqueue(new Callback<Void>() {
// onResponse and onFailure here
}
For example without the Accept header this is the response in the Logcat
D/OkHttp: --> GET http://192.168.0.6:8000/api/logout
D/OkHttp: Token: wE1Y8IxJpwyXtvw0fYoXZAlQ6qCx24YtzonQIeJBQSHmNppe0Sn1kLYDgZKCw4MKbpab4Vspf61Nzer1
D/OkHttp: --> END GET
D/OkHttp: <-- 200 OK http://192.168.0.6:8000/login
//a bunch of html that's the web page at this route, notice the /api is missing
How can I send this correctly?
EDIT:
I"m using a Laravel project for the backend and this is the relevant route
Route::middleware('auth:sanctum')
->get('/logoutApi', function (Request $request) {
$request->user()->tokens()->delete();
return response("LoggedOut", 202);
});
create class Authenticator, like:
const val HEADER_TOKEN_FIELD = "Authorization"
class ClassAuthenticator(
private val pref: SharedPref
) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
return response.request().newBuilder()
.header(HEADER_TOKEN_FIELD, pref.getToken())
.build()
}
}
then add interceptor in your client with:
val httpClient = OkHttpClient.Builder()
.authenticator(ClassAuthenticator(pref))
.addInterceptor { chain ->
val request = chain.request()
val httpRequest = request.newBuilder()
.addHeader(HEADER_TOKEN_FIELD,
"Bearer ${pref.getToken()}")
.build()
val response = chain.proceed(httpRequest)
response
}
.build()
I have a strange problem with one of my retrofit call,it works fine when the app is in the background(recent list)
I have a call through which i update my widget data,the problem is when the app is cleared of from the recent list,the call gives HTTP 401 unauthorized response.
however i pass the same bearer token with it.
please have a look at the code and suggest some help
public static OkHttpClient getOkhttpClient() {
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + TokenGenerator.getToken())
.build();
return chain.proceed(newRequest);
}
}).build();
return client;
}
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(getOkhttpClient())
.addConverterFactory(JacksonConverterFactory.create())
.build();
}
return retrofit;
}
im trying to consume an api that has that authorization header, i can get a 200 response in Postman with all data but cant get it to work in retrofit
May be you need add the Token using OkHttp Interceptor.
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(mTokenInterceptor)
.build();
then add it to Retrofit:
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(base_url)
.build();
the mTokenInterceptor:
Interceptor mTokenInterceptor = new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (mToken != null) {
Request.Builder requestBuilder = request.newBuilder()
.addHeader("Authorization", mToken);
Request newRequest = requestBuilder.build();
return chain.proceed(newRequest);
}
return chain.proceed(request);
}
};
when you get the Token, just assign the mToken,
You can try something like below, just a crude example
#GET("your server url goes here")
Call<Your_Model_Class> getServerData(#Header("Authorization") String token);
Pass your token to getServerData method.
I am using Retrofit 2.1.0 for API parsing in my android application. I need the time taken by retrofit to parse the API.
How to obtain the Request/ response time using Retrofit 2.
Below is the Retrofit Rest Client Call I am using for API parsing.
public static class ServiceGenerated {
static OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder ongoing = chain.request().newBuilder();
return chain.proceed(ongoing.build());
}
})
.build();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(RetrofitUtils.API_BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create());
public static <S> S createService(Class<S> serviceClass) {
Retrofit retrofit = builder.client(httpClient).build();
return retrofit.create(serviceClass);
}
}
You can calculate the total round trip time by substracting the timestamp when response is received and timestamp when the request is sent. These two methods from the Response object will give you these two values
Response.sentRequestAtMillis()
Response.receivedResponseAtMillis()
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
long tx = response.sentRequestAtMillis();
long rx = response.receivedResponseAtMillis();
System.out.println("response time : "+(rx - tx)+" ms");
It's easy you can find the receivedResponseAtMillis() and the sendResponseAtMillis() in the raw() part of the response and then you can calculate the difference
response.raw().receivedResponseAtMillis()
response.raw().sendResponseAtMillis()
Try this
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(httpLoggingInterceptor);
}
also add
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
to your app module gradle file
Sample response:
Date: Fri, 12 Aug 2016 07:33:23 GMT
OkHttp-Sent-Millis: 1470987074283
OkHttp-Received-Millis: 1470987074422
I have a REST Server, My problem is whenever my response is not successful i want to parse the error from response body and show it to the user (pass the error info to the calling Activity)
#Named("rest_api")
#Singleton
#Provides
Interceptor provideRESTInterceptor(final UserManager userManager) {
return new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
String token = userManager.getJwt();
Request request = original.newBuilder()
.header("Accept", "application/json")
.header("Authorization", "Bearer "+token)
.method(original.method(), original.body())
.build();
Log.i(LOG_TAG, "REQUEST URL "+request.uri());
Response response = chain.proceed(request);
if(!response.isSuccessful()) {
// How do i send this error to my Activity.
APIError apiError = new APIError(response.code(),response.body().string());
}
// Customize or return the response
return response;
}
};
}
I am using RxJavaConverter
#Named("rest_api")
#Singleton
#Provides
Retrofit provideRESTRetrofit(#Named("rest_api")OkHttpClient client, Converter.Factory converter, CallAdapter.Factory rxJava) {
return new Retrofit.Builder()
.baseUrl(PUBLIC_PRODUCTION_URL)
.client(client)
.addCallAdapterFactory(rxJava)
.addConverterFactory(converter)
.build();
}
Since Retrofit 2.0, even if the response is not successful it tries to convert the data with given GSON into (POJO), and thus throw an error, and i lost the actual message of the error.
As #david:mihola suggested it worked, using Retrofit Response object i was able to achieve this.
Response<MyObject> response;
if(response.isSucessful) {
MyObject obj = response.body();
} else {
// You can process your error message from the reponse
}
It really saved me from lot manual GSON parsing