In all requests in the application, if an error occurs 401, need to perform a certain action.
I do not want to handle this action in every request manually.
Is it possible to solve this moment at the OkHttp level so that it immediately applies to all requests at once?
Retrofit and OkHttp class:
public class RestApi {
public final User user;
private PreferenceHelper preferenceHelper;
public static final String TAG = "RestApi: ";
#Inject
public RestApi(PreferenceHelper preferenceHelper) {
this.preferenceHelper = preferenceHelper;
TokenAppendingHeaderInterceptor tokenInterceptor = new TokenAppendingHeaderInterceptor();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(tokenInterceptor)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder().baseUrl(Const.Url.API)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
user = retrofit.create(User.class);
}
public class TokenAppendingHeaderInterceptor implements Interceptor {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String token = preferenceHelper.getToken();
Request newRequest = request.newBuilder()
.addHeader(Const.Url.COOKIE, token)
.build();
return chain.proceed(newRequest);
}
}
public String getCookiesFromResponse(Response response) {
String cookies = "";
List<String> listCookies;
try {
listCookies = response.headers().toMultimap().get("Set-COOKIE");
cookies = CookieHelper.getStringCookies(listCookies);
} catch (NullPointerException e) {
Log.d(TAG, "getCookiesFromResponse: BITRIX can't send cookies");
} finally {
return cookies;
}
}
}
Add Headers for Authentication in request
In your code TokenAppendingHeaderInterceptor class in method Intercept add the hweaders like this
r
equest.addheader('Content-Type': 'application/json;charset=UTF-8');
request.addheaders('Authorizcation', 'Basic '+btoa(username + ':' + password));
I think it will help you
Related
I am using JWT authentication and storing the auth token in the shared preference. I am not able to find a way to add authorization header to the retrofit client . That's why I am getting 401 errors for my network call the first time , from the second time it works. How to solve it ?
#Module
public class AppRetrofitModule {
private static final String TAG = "AppRetrofitModule";
private static Retrofit.Builder builder
= new Retrofit.Builder()
.baseUrl(Config.REST_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();
private static OkHttpClient.Builder httpClient
= new OkHttpClient.Builder();
private static HttpLoggingInterceptor logging
= new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BASIC);
#Singleton
#Provides
public Retrofit provideRetrofit(AppPreferencesHelper appPreferencesHelper) {
String authToken = "Bearer " + appPreferencesHelper.getAccessToken();
Log.d(TAG, "provideRetrofit: " + authToken);
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Response response = chain.proceed(original);
Request request = original.newBuilder()
.header("Authorization", authToken)
.method(original.method(), original.body()).build();
return chain.proceed(request);
}
});
if (!httpClient.interceptors().contains(logging)) {
httpClient.addInterceptor(logging);
httpClient.connectTimeout(60, TimeUnit.SECONDS);
httpClient.callTimeout(60, TimeUnit.SECONDS);
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit;
}
}
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Response response = chain.proceed(original);
String authToken = "Bearer " + appPreferencesHelper.getAccessToken();
Request request = original.newBuilder()
.header("Authorization", authToken)
.method(original.method(), original.body()).build();
return chain.proceed(request);
}
});
if (!httpClient.interceptors().contains(logging)) {
httpClient.addInterceptor(logging);
httpClient.connectTimeout(60, TimeUnit.SECONDS);
httpClient.callTimeout(60, TimeUnit.SECONDS);
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit;
Previously I was making the mistake of getting the authToken outside the interceptor. But it needs to be fetched inside the interceptor such that we can get the token at the first time too. It was working before for the second API call because the authToken value gets refreshed.
I am working on a project which will retrieve and send data to server through django api. I am facing problem to POST data through the api. My codes are given below. Executing this code give 401 Error in response in android studio but the api works fine in postman or web browser. TIA for the help.
Class to set retrofit instance:
public class ApiClient {
public static final String BASE_URL = "https://myapilink.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
final OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "auth-value"); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "Basic YWRtaW46MTIzNA=="); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
})
.build();
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.client(okHttpClient)
.build();
}
return retrofit;
}
Retrofit interface:
public interface ApiInterface {
#Headers("Content-Type: application/json")
#POST("/api/auth/login/")
Call<LoginModel> getLoginResponseWithRetrofit(#Body LoginBody loginBody);
}
LoginBody model:
public class LoginBody {
String email;
String password;
public LoginBody(String email, String password){
this.email=email;
this.password=password;
}
}
Main api calling method:
public void postDataWithRetrofit(String email, String password){
//modelFromIDProvider.add(InternalDataProvider.getInstance().getAgentDataModelList());
int selectedPosition,id;
LoginBody loginBody = new LoginBody(email, password);
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<LoginModel> call;
Map<String, String> formData = new HashMap<>();
formData.put("email", email);
formData.put("password", password);
Log.d(TAG,"formdata "+formData.toString());
//call = apiService.getLoginResponseWithRetrofit(email, password);
call = apiService.getLoginResponseWithRetrofit(loginBody);
call.enqueue(new Callback<LoginModel>() {
#Override
public void onResponse(Call<LoginModel> call, Response<LoginModel> response) {
Log.d(TAG,"isSuccessful "+response.isSuccessful()+" code "+response.code());
Log.d(TAG,"Data from response : "+response.body());
if(!response.isSuccessful()) {
dialog.dismiss();
return;
}
LoginModel responseModel = response.body();
InternalDataProvider.getInstance().setLoginDataModel(responseModel);
Log.d(TAG,"Data from response : "+response.body());
}
#Override
public void onFailure(Call<LoginModel> call, Throwable t) {
if(call.isCanceled()){
Log.d(TAG," call cancelled "+t.toString());
return;
}
Log.d(TAG," inside onFailure "+t.toString());
}
});
}
I have a class to handle token refreshes once they expire. The code is below:
public class TokenAuthenticator implements Authenticator {
#Nullable
#Override
public synchronized Request authenticate(#NonNull Route route, #NonNull Response response) throws IOException {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<User> call = apiInterface.refreshTokens(new ClientRequest(Songa.getContext().getString(R.string.client_id),
App.getContext().getString(R.string.client_secret),
App.getContext().getString(R.string.grant_type), getRAGUser().getRefreshToken()));
User ragUser = call.execute().body();
if (ragUser != null) {
Gson gson = new Gson();
String user = gson.toJson(ragUser);
PrefUtils.putString(Constants.USER, user);
long tokenExpiryPeriod = System.currentTimeMillis() + Long.parseLong(ragUser.getExpiryPeriod());
PrefUtils.putLong(Constants.TOKEN_EXPIRY_PERIOD, tokenExpiryPeriod);
return response.request().newBuilder().header("Authorization", "Bearer " + ragUser.getAccessToken()).build();
} else {
if (responseCount(response) >= 3) {
Log.e("TokenAuthenticator", String.valueOf(responseCount(response)));
//we have failed 3 times; log the user out
EventBus.getDefault().post(new LogoutEvent());
return null;
}
}
return null;
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
My intention is that once a token expires, the authenticator should retry a maximum of three times before giving up and logging out the user. However, the code below executes each request three times, even with a valid token.
I've always assumed that the Authenticator class only steps in when the token expires but from my logs, I can see that it is called every time a new request is made.
The following is the code from my Retrofit client:
public class RestClient {
private static final String BASE_URL = "https://my.base.url/api/v3/";
private static String token = "Bearer " + getAccessToken();
private static Retrofit retrofit = null;
public RestClient() {
}
public static Retrofit getClient() {
if (retrofit == null) {
TokenAuthenticator tokenAuthenticator = new TokenAuthenticator();
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(1);
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}).create();
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.authenticator(tokenAuthenticator)
.addInterceptor(loggingInterceptor)
.addInterceptor(chain -> {
Request original = chain.request();
Request request = original.newBuilder()
.addHeader("Authorization", token)
.addHeader("Content-Type", "application/json")
.build();
return chain.proceed(request);
})
.addInterceptor(loggingInterceptor)
.dispatcher(dispatcher)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okClient)
.build();
}
return retrofit;
}
}
Is there a better way of implementing token authentication with my requirements; 3 retries before logout?
I am trying to use an Interceptor to add a header when using Retrofit. I think I have created my Interceptor in the right way but I don't know what should I do to call it and connect it with my GET Retrofit method.
This is my Interceptor:
public class HeaderInterceptor
implements Interceptor {
#Override
public Response intercept(Chain chain)
throws IOException {
Request request = chain.request();
request = request.newBuilder()
.addHeader(Constants.VersionHeader.NAME, Constants.VersionHeader.VALUE)
.addHeader("Authorization", "Bearer " + token)
.addHeader("Origin","MY URL")
.build();
Response response = chain.proceed(request);
return response;
}
}
And this is my interface:
public interface CategoryService {
#GET("/v3/projects/{projectId}/categories/")
Call<ArrayList<Category2>> getProjectCategories(#Path("projectId") String projectId);
}
I also have this client which I don't know if I should use it anymore considering that I am using an Interceptor:
public class CategoryClient {
public static final String BASE_URL = "MY URL";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
So I have this GET method getProjectCategories, where I pass the projectID and it returns the contents. What I want to know is how can I call the method using the Interceptor and be able to get the results from the request.
I was able to fix my problem by creating a method called SendNetworkRequest sending the projectId as a parameter, and inside this class I created my OkHttpClient, my Interceptor and my retrofit builder to handle everything that i needed.
private void SendNetworkRequest(String projectID) {
OkHttpClient.Builder okhttpBuilder = new OkHttpClient.Builder();
okhttpBuilder.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder newRequest = request.newBuilder().header("Authorization", "Bearer " + token);
return chain.proceed(newRequest.build());
}
});
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("MY URL")
.client(okhttpBuilder.build())
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
CategoryService category = retrofit.create(CategoryService.class);
Call<ArrayList<Category2>> call = category.getProjectCategories(projectID, token);
call.enqueue(new Callback<ArrayList<Category2>>() {
#Override
public void onResponse(Call<ArrayList<Category2>> call, Response<ArrayList<Category2>> response) {
listCategories = response.body();
listCategories.remove(response.body().size() - 1);
if (response.body().size() > 0){
add_category_layout.setVisibility(View.VISIBLE);
layout_bar.setVisibility(View.VISIBLE);
message_body.setVisibility(View.INVISIBLE);
message_title.setVisibility(View.INVISIBLE);
edit_image.setVisibility(View.INVISIBLE);
adapter2 = new CategoryAdapter2(getApplicationContext(), listCategories);
recyclerView.setAdapter(adapter2);
recyclerView.setVisibility(View.VISIBLE);
}
}
#Override
public void onFailure(Call<ArrayList<Category2>> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
I am using Retrofit 2 and Okhttp for my android project. I want to add multiple headers in the api request.
This is my interceptor code :
public class NetworkInterceptors implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("Userid", "10034")
.addHeader("Securitykey", "Fb47Gi")
.build();
return chain.proceed(request);
}
}
This is not working properly. In server side I am getting only the last added header (in the above example I am getting only Securitykey missing "Userid" )
Please Help.
Thanks for support
I found the answer, This is working fine for me
public class NetworkInterceptors implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Request newRequest;
newRequest = request.newBuilder()
.addHeader("Userid", "10034")
.addHeader("Securitykey", "Fb47Gi")
.build();
return chain.proceed(newRequest);
}
}
You can use this class pass the context in this class if user already logged in.
public class ApiClient {
public static final String BASE_URL = "";
private static Retrofit retrofit = null;
static Context mcontext;
public static Retrofit getClient(Context context,String baseUrl)
{
mcontext = context;
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(220, TimeUnit.SECONDS)// Set connection timeout
.readTimeout(220, TimeUnit.SECONDS)// Read timeout
.writeTimeout(220, TimeUnit.SECONDS)// Write timeout
.addInterceptor( HeaderInterceptor() )
// .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)// Add cache interceptor
// .cache(cache)// Add cache
.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
private static Interceptor HeaderInterceptor() {
return new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Request request = chain.request();
if(SharedPreference.getlogin(mcontext).equals("")){
request = request.newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer "+SharedPreference.gettoken(mcontext))
.build();
}
else {
request = request.newBuilder()
.addHeader("Accept", "application/json")
.build();
}
okhttp3.Response response = chain.proceed(request);
return response;
}
};
}
}