I'm using Retrofit and OkHttp in my Project and i want to refresh token when the server gives 401 error code and store the new token in shared preference and make the same call again without notifing the user. Token type is bearer and expires every hour.
But the output that i desired is not coming, when the token expires the authenticator does nothing.
ApiInterface.java
public interface ApiInterface {
#FormUrlEncoded
#POST("auth/login")
Call<ResponseBody> login(#FieldMap HashMap<String, Object> map);
#FormUrlEncoded
#POST("auth/register")
Call<ResponseBody> register(#FieldMap HashMap<String, Object> map);
#FormUrlEncoded
#POST("auth/forgotpassword")
Call<ResponseBody> forgotPassword(#FieldMap HashMap<String, Object> map);
#POST("auth/refresh")
Call<ResponseBody> refreshToken(#Header("Authorization") String token);
#POST("auth/GetAllInterests")
Call<ResponseBody> getAllInterest();
#POST("auth/AddUserProfile")
#Multipart
Call<ResponseBody> addUserProfile(
#Part("display_name") RequestBody display_name,
#Part("children_age_show") RequestBody children_age_show,
#Part("address") RequestBody address,
#Part("no_of_children") RequestBody no_of_children,
#Part("children_age") RequestBody children_age,
#Part("date_of_birth") RequestBody date_of_birth,
#Part("interests") RequestBody interests,
#Part List<MultipartBody.Part> images
);
#POST("auth/GetUserProfile")
Call<ResponseBody> getUserProfile();
#FormUrlEncoded
#POST("auth/ChangePassword")
Call<ResponseBody> changePassword(#FieldMap HashMap<String, Object> map);
#POST("auth/logout")
Call<ResponseBody> logout();
}
ApiClient.java
public class ApiClient {
private static Retrofit retrofit = null;
private static ApiInterface apiInterface = null;
public static Retrofit getRetrofit(Context context) {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (AppPreference.getPreference(context, AppPersistence.keys.AUTH_TOKEN) != null) {
request = request.newBuilder()
.addHeader("Authorization", AppPreference.getPreference(context, AppPersistence.keys.AUTH_TOKEN))
.build();
}
return chain.proceed(request);
}
})
.authenticator(new TokenAuthenticator(context))
.build();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
return retrofit;
}
public static ApiInterface getApiInterface(Context context) {
if (apiInterface == null) {
getRetrofit(context);
apiInterface = retrofit.create(ApiInterface.class);
}
return apiInterface;
}
}
TokenAuthenticator.java
public class TokenAuthenticator implements Authenticator {
Context context;
public TokenAuthenticator(Context context) {
this.context = context;
}
#Override
public Request authenticate(Route route, Response response) throws IOException {
Log.d("Authenticator", "Authenticator Called");
if (response.code() == 401) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Log.d("Authenticator", "Requesting for New Token");
ApiInterface apiInterface = retrofit.create(ApiInterface.class);
retrofit2.Response<ResponseBody> response1 = apiInterface
.refreshToken(AppPreference.getPreference(context, AppPersistence.keys.AUTH_TOKEN))
.execute();
if (response1.isSuccessful()) {
try {
String body = response1.body().string();
JSONObject jsonObject = new JSONObject(body);
if (jsonObject.getBoolean("status")) {
String newToken = "Bearer " + jsonObject.getJSONObject("Data").getString("access_token");
AppPreference.setPreference(context, AppPersistence.keys.AUTH_TOKEN, newToken);
Log.d("Authenticator", "New Token Generated");
return response.request().newBuilder()
.header("Authorization", newToken)
.build();
}
} catch (Exception e) {
Log.e("TokenAuthenticator", e.getMessage(), e);
}
}
Toast.makeText(context, response1.body().string(), Toast.LENGTH_SHORT).show();
}
return null;
}
}
Please try the below way
(1) HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
try {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = chain.proceed(request);
if (response shows expired token) {
// get a new token (I use a synchronous Retrofit call)
// create a new request and modify it accordingly using the new token
Request newRequest = request.newBuilder().build();
// retry the request
return chain.proceed(newRequest);
}
// otherwise just pass the original response on
return response;
}
});
} catch (Exception e) {
e.printStackTrace();
}
String baseUrl = "";
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
apiService = retrofit.create(ApiService.class);
(2) public class TokenAuthenticator implements Authenticator {
private final ApiService apiservice;
public TokenAuthenticator(TokenServiceHolder tokenServiceHolder) {
this.tokenServiceHolder = tokenServiceHolder;
}
#Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
newtoken = apiservice.refreshToken().execute();
return response.request().newBuilder()
.header(AUTHORIZATIONKEY, newtoken)
.build();
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
// Null indicates no attempt to authenticate.
return null;
}
}
Hope it may help you
Related
I'm trying to add a header to a simple okhttp (Get) request. How do I add the HttpHeader properly? Can I debug to ensure that my Header is actually sent to the server?
Request request = new Request.Builder()
.url("URL")
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request newRequest = originalRequest.newBuilder()
.addHeader("Header", "123")
.build();
return chain.proceed(newRequest);
}
})
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
I've looked for basic simple examples but they are with Retrofit, GSON, Interfaces, or in Kotlin. Need to understand it codewise.
You can use by method addHeader send chain as param and add headers.
Request getRequest = chain.request();
Request.Builder requestBuilder = getRequest.newBuilder()
.addHeader("Header", "123");
Request request = requestBuilder.build();
return chain.proceed(request);
You can also visit and look at the answers link1 and link2.
Here is the all-request Structure you can use.
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
};
OkHttpClient client = httpClient.build();
Request request = new Request.Builder()
.url("URL")
.addHeader("Header", "123")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Log.d("OKHTTP3", e.getMessage());
// You get this failure
runOnUiThread(() -> {
});
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
final String _body = response.body().string();
Log.d("OKHTTP3", _body);
runOnUiThread(() -> {
});
} catch (InterruptedIOException e) {
runOnUiThread(() -> {
// Or this exception depending when timeout is reached
});
}
}
});
To check your request and to add headers, you can use interceptors.
To add headers, (copied from gist):
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("User-Agent", "Your-App-Name")
.header("Accept", "application/vnd.yourapi.v1.full+json")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
To see your headers, you can use sample example provided here:
class LoggingInterceptor implements Interceptor {
#Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
Use addHeader() to add headers. header() sets the already added header name to the value.
Request newRequest = originalRequest.newBuilder()
.addHeader("Header", "123")
.build();
And to verify it's working correctly, you can use HttpLoggingInterceptor to log your network requests.
I'm trying to upload a photo to the server.
My problem is that I can not get a link to this picture, which the server returns to me.
public class RetrofitClient {
public static Retrofit createService(
String username, String password) {
String authToken = Credentials.basic(username, password);
return createService(authToken);
}
public static Retrofit createService(final String authToken) {
AuthenticationInterceptor interceptor = null;
if (!TextUtils.isEmpty(authToken))
interceptor = new AuthenticationInterceptor(authToken);
Gson gson = new GsonBuilder()
.setLenient()
.create();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(logging)
.retryOnConnectionFailure(true)
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS).build();
return new Retrofit.Builder()
.baseUrl(AppConstant.BASE_URL+"/")
.client(client)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(ScalarsConverterFactory.create())
.build();
}
public static RetroInterface getApiServices(String user_name, String passowrd) {
return createService(user_name, passowrd).create(RetroInterface.class);
}
}
RetroInterface.java
....
#Multipart
#POST("parents/profile/images")
Call<String>
uploadParentProfilePicture2(#Part MultipartBody.Part filePart);
....
Profile.java
....
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));
RetroInterface retroInterface = RetrofitClient.getApiServices(getEmail(), getPassword());
Call<String> call = retroInterface.uploadParentProfilePicture(filePart);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NonNull Call<String> call, #NonNull Response<String> response) {
Log.d("ldsfjjlk", "response: " + response.body());
}
#Override
public void onFailure(#NonNull Call<String> call, #NonNull Throwable t) {
Log.d("ldsfjjlk", "Throwable: " + t.getLocalizedMessage());
}
});
....
HttpLoggingInterceptor response :
"parents/654/profile/images/1530979485020.jpg"
It is right!
My log:
"parents"
What is this?
Something wrong with Retrofit?
Also I tried to replace
Call<String> to Call<ResponseBody> but:
response.body.string() == null
What did I miss?
I'm using retrofit and I need to upload and image, but I'm getting status code 400.
Here's the code.
This is the interface.
public interface SupportInterface {
//Get request for sending photo in chat
#Multipart
#POST("/api/upload-image")
Call<ResponseBody> getChatPhoto(#Header("Content-Type") String json,
#Header("Authorization") String token,
#Header("Cache-Control") String cache,
#Part("type") String type,
#Part("user_id") String userId,
#Part MultipartBody.Part image_path);
}
I'm using headers + I need to send user_id and type too. So I'm using #Part. I done right yes?
Here's the retrofit in initialization part.
public class ApiClient {
private static ApiClient instance;
OkHttpClient.Builder client = new OkHttpClient.Builder()
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS);
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request request = chain.request();
request = request.newBuilder()
.header("Cache-Control", "public, max-age=0")
.build();
return chain.proceed(request);
}
});
supportopApi = new Retrofit.Builder()
.baseUrl(endpoint)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(SupportopApi.class);
}
public Call<ResponseBody> getChatImage(MultipartBody.Part multipartBody) {
return supportopApi.getChatPhoto("application/json", There is my accessToken,
"no-cache", "5", This is userID, multipartBody);
}
}
If I done here something wrong please tell me.
And here's the main part.
public void getChatImage() {
File file = new File("/storage/emulated/0/Download/s-l640.jpg");
RequestBody reqFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part multiPartFile = MultipartBody.Part.createFormData("image", file.getName(), reqFile);
Call<ResponseBody> chatImageCall = apiClient.getChatImage(multiPartFile);
chatImageCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
Log.d(TAG, response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
} else {
Toast.makeText(context, "Response is not successful: " + response.errorBody(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(getActivity(), "An error occurred", Toast.LENGTH_SHORT).show();
}
});
}
I'm getting here bad request 400.
#Header("Content-Type") String json
You declare your content type is JSON but actually you pass to server the multipart (form data)
So I think you're trying to do:
#Header("Accept") String json
(accept JSON from server)
If I log in on my website I get a cookie. That cookie is one year valid and I want it to implement in my Android application. With that cookie I can get JSON data and it works in Postman with the Interceptor extension.
But in Android I can't get it working. I tried it on many different ways:
Retrofit2:
public void GetCaregiver(){
CookieInterceptor interceptor = new CookieInterceptor();
interceptor.setSessionCookie("eyJpdiI6IjVcL1d6MEhLaGNZV3V2MFdVdFRtRVN3PT0iLCJ2YWx1ZSI6IldZWU42cUZQblcwejlDMk5LVllYOENXSThmVk9UUFIzaStxclZoUTFIRGlMazFlUzdjVDhFcTVxRllwMERncFFLZTVNQlQ0VUQ2SG5BSUE3TzJcL0Vrdz09IiwibWFjIjoiNDk3YmY3ZmE0NzA2Nzk1NzMwNTM0NDViOGNjNDllN2ZmZmYyNmNhNTdjNWYwOWYwYTIzZmI2YmI5OTc3OWZjNiJ9");
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(zorggemakURL)
.callFactory(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
RESTInterface service = retrofit.create(RESTInterface.class);
Call<List<Caregiver>> call = service.getCaregivers();
call.enqueue(new Callback<List<Caregiver>>() {
#Override
public void onResponse(Call<List<Caregiver>> call, retrofit2.Response<List<Caregiver>> response) {
Log.d("Response", response.toString());
}
#Override
public void onFailure(Call<List<Caregiver>> call, Throwable t) {
Log.d("Response", t.toString());
}
});
}
Volley:
public void GetCaregiver() {
// the request
String url = zorggemakURL + "api/user/overview";
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET,
url,
null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
// response
Log.d("Response", response.toString());
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
Log.d("ERROR", "error => " + error.toString());
}
}) {
#Override
public String getBodyContentType() {
return "application/x-www-form-urlencoded";
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "application/json");
String creds = String.format("%s:%s", "orlandosmits", "test123");
String auth = "Basic " + Base64.encodeToString(creds.getBytes(), Base64.DEFAULT);
headers.put("Cookie", cookie);
headers.put("Authorization", auth);
return headers;
}
};
Volley.newRequestQueue(context).add(request);
}
I will always get the HTML page back and not the JSON data. Anyone have an idea?
I got it working.
Final code:
public void GetCaregiver() {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HeaderInterceptor()).build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(zorggemakURL)
.callFactory(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
RESTInterface service = retrofit.create(RESTInterface.class);
Call<List<Caregiver>> call = service.getCaregivers();
call.enqueue(new Callback<List<Caregiver>>() {
#Override
public void onResponse(Call<List<Caregiver>> call, retrofit2.Response<List<Caregiver>> response) {
List<Caregiver> caregivers = response.body();
String test = "";
}
#Override
public void onFailure(Call<List<Caregiver>> call, Throwable t) {
Log.d("Response", t.toString());
}
});
}
HeaderInterceptor class:
public class HeaderInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain)
throws IOException {
Request request = chain.request();
request = request.newBuilder()
.addHeader("Cookie", "laravel_session=eyJpdiI6Im54UEpmbW9qdlc0a01FdG1uSGdNU3c9PSIsInZhbHVlIjoieGNuck96K0V2UWU1aG1vVE9DRDRQeWNQbEJ0UldEdWt2TUxFWjByVGVxckZ1a1NhcnE5dXMxTDBqTGtrcExQVEwzWkNQcTNSeHpUVXpFU3A5NHYxdEE9PSIsIm1hYyI6IjhlMzY0ODllYzEwOTFkNTYzZDU2ZmY1NTM5YTUyNmE0MTVlY2JmMjJkZDk0YmFiZmVhNDJmZDNkZmMwMTg5YWIifQ%3D%3D")
.build();
Response response = chain.proceed(request);
return response;
}
I have trouble with the design of my Retrofit interface creator. I want to be able to instanciate the API interface in a generic way and update the corresponding instance whenever a token is passed. Currently, when I update the token, I have to call createService() method again to get the new instance that used the token in the generation of the Interface...
Somebody asked for a similar question but never got an answer here
public class RetrofitCreator {
private static String TAG = "RetrofitCreator";
private static String WSSE = null;
private static String AmzToken = null;
static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static AmazonAPI amazonAPI = null;
private static VanishAPI cobaltAPI = null;
//static OkHttpClient client = new OkHttpClient.Builder().build();
static OkHttpClient.Builder httpClient = new OkHttpClient.Builder().addInterceptor(interceptor.setLevel(HttpLoggingInterceptor.Level.BODY));
private static Retrofit.Builder builder =
new Retrofit.Builder();
public static <S> S createService(Class<S> serviceClass) {
S mAPI = null;
if(serviceClass.getSimpleName().equals("VanishAPI")){
if(VanishAPI==null){
VanishAPI = (VanishAPI) createVanishAPI(serviceClass);
}
mAPI = (S) VanishAPI;
}else if(serviceClass.getSimpleName().equals("AmazonAPI")){
if(amazonAPI==null){
amazonAPI = (AmazonAPI) createAmazonAPI(serviceClass);
}
mAPI = (S) amazonAPI;
}
return mAPI;
}
public static void setWSSE(String WSSE) {
RetrofitCreator.WSSE = WSSE;
vanishAPI = createVanishAPI(VanishAPI.class);
}
public static void setAmzToken(String token) {
RetrofitCreator.AmzToken = token;
amazonAPI = createAmazonAPI(AmazonAPI.class);
}
private static <S> S createAmazonAPI(Class<S> serviceClass){
httpClient = getUnsafeOkHttpClient();
builder = new Retrofit.Builder()
.baseUrl(Constants.URL_AMAZON)
.addConverterFactory(JacksonConverterFactory.create());
if (AmzToken != null) {
Log.w(TAG, "WSSE not null!");
Interceptor interceptorSecure = new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Cache-Control", "no-cache")
.header("Accept", "application/json")
.header("Authorization", "Bearer " + AmzToken)
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
};
httpClient.addInterceptor(interceptorSecure);
}
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
(...)
}
To get it in each Activity I use :
amazonApi = RetrofitCreator.createService(AmazonAPI.class);
Make your interceptor look like this:
public class TokenInterceptor implements Interceptor {
private String token;
public String getToken() {
return token;
}
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Cache-Control", "no-cache")
.header("Accept", "application/json")
.method(original.method(), original.body());
if (getToken() != null) {
requestBuilder.header("Authorization", "Bearer " + AmzToken);
}
Request request = requestBuilder.build();
return chain.proceed(request);
}
public void setToken(String token) {
this.token = token;
}
}
Add it to you OkHttp client and keep reference to this interceptor.
Now you don't have to call createService() after each token change. Just change token in interceptor via interceptor.setToken()