I'd like to intercept all responses received by the retrofit engine, and scan for HTTP error code, for example error 403.
I'm aware I can use the failure(RetrofitError error) callback of every request and check for 403's but I'd like to wrap the response globally.
I can see that request interception is possible, but I do not see a similar option for response.
Any suggestions?
I was able to accomplish that by adding an interceptor to the OkHttpClient that retrofit is using.
Kotlin + Retrofit 2.x
val clientBuilder = OkHttpClient.Builder()
clientBuilder.addInterceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
if (response.code() == 403) {
handleForbiddenResponse()
}
response
}
Retrofit 2.x:
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.
addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.code() == 403) {
handleForbiddenResponse();
}
return response;
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(clientBuilder.build();)
.build();
Retrofit 1.x:
public class ForbiddenInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.code() == 403) {
handleForbiddenResponse();
}
return response;
}
}
OkHttpClient okHttpClient = Utils.createUnsafeOkHttpClient();
okHttpClient.interceptors().add(new ForbiddenInterceptor());
RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder()
.setEndpoint(API_BASE_URL)
.setClient(new OkClient(okHttpClient));
Another possibility is to override the execute method in Retrofit's OkClient. In my case I was looking for a specific response header, so it went something like this:
OkClient client = new OkClient(okHttpClient) {
#Override
public retrofit.client.Response execute(retrofit.client.Request request) throws IOException {
retrofit.client.Response response = super.execute(request);
// Do whatever with 'response', such as looking for the value of a particular header:
List<retrofit.client.Header> headers = response.getHeaders();
for (retrofit.client.Header header : headers) {
if (MY_HEADER.equalsIgnoreCase(header.getName())) {
this.savedValue = header.getValue();
break;
}
}
return response;
}
};
RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder()
.setEndpoint(API_BASE_URL)
.setClient(client);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
okhttp3.Response response = chain.proceed(request);
// todo deal with the issues the way you need to
if (response.code() == 500) {
startActivity(
new Intent(
ErrorHandlingActivity.this,
ServerIsBrokenActivity.class
)
);
return response;
}
return response;
}
})
.build();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("http://your_url")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
As you can see in the snippet above, the okhttp3.Response response = chain.proceed(request); line accesses the server response. Consequently, we can check the status code with if (response.code() == 500) and then open the ServerIsBrokenActivity.
Related
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 Singleton dagger module for OkHttp client and I am trying to add header using Interceptor
#Provides
#Singleton
OkHttpClient provideOkhttpClient(Cache cache, final LocalData localData) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(logging);
client.addNetworkInterceptor(new Interceptor() {
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Hp-Application", "Android");
Request request = requestBuilder.build();
Response originalResponse = chain.proceed(request);
try {
if (originalResponse.code() == 200) {
localData.setLastUpdateTime(System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
return originalResponse;
}
});
client.connectTimeout(60, TimeUnit.SECONDS);
client.cache(cache);
return client.build();
}
But looking to the logs I can't see expected header. Also I receive error, as specific call don't work without required header.
I also tried to add it with addInterceptor()/addNetworkInterceptor() using different class
public class HeaderInterceptor
implements Interceptor {
#Override
public Response intercept(Chain chain)
throws IOException {
Request request = chain.request();
request = request.newBuilder()
.addHeader("Hp-Application", "Android")
.build();
return chain.proceed(request);
}
}
But this way didn't work for me too.
How can I add this header to each call of application having only one implementation?
The order you add the interceptors matters. Your logging interceptor runs first, and only after that is the header-adding interceptor run.
For best logging experience, make the logging interceptor the last one you add.
Hey #Igor try this snippet this might help
public class RetrofitClient {
private static String BASE_URL = "http://192.168.0.100/rest/main.php/";
private static Retrofit retrofit = null;
public static Retrofit getRetroftInstance() {
if (retrofit == null) {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addNetworkInterceptor(new SessionRequestInterceptor());
httpClient.addNetworkInterceptor(new ReceivedCookiesInterceptor());
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
}
return retrofit;
}}
public class ReceivedCookiesInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
HashSet<String> cookies = new HashSet<>();
for (String header : originalResponse.headers("Set-Cookie")) {
cookies.add(header);
if(header.startsWith("XSRF-TOKEN")) {
String newCookie[]=header.split(";");
System.out.println("newCookie Length: "+newCookie.length);
for(String ss:newCookie) {
if(ss.startsWith("XSRF-TOKEN")) {
System.out.println("Cookies ss: " + ss);
sharedPrefs.setToken(ss);
}
}
}
}
}
return originalResponse;
}
}
public class SessionRequestInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder request = original.newBuilder();
request.header("Cookie",ServiceSharedPrefs.getInstance().getToken()));
request.method(original.method(), original.body());
return chain.proceed(request.build());
}
}
new OkHttpClient.Builder()
.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(AUTHENTICATION_HEADER, AUTHENTICATION_KEY).
method(original.method(), original.body());
Request request = requestBuilder.build();
//System.out.println(request.toString());
return chain.proceed(request);
}
}).addInterceptor(logging)
.build();
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;
}
};
}
}
In previous retrofit versions it was possible to add an interceptor and use that to add query parameters that were globally needed for example:
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade requestFacade) {
requestFacade.addQueryParam("platform", "android");
requestFacade.addQueryParam("app_version", com.package.BuildConfig.VERSION_NAME);
}
})
With the new implementation, it's required that you use OkHttpClient.interceptors. With this new approach, how would one append parameters without removing the original paramaters?
Here is an okhttp implementation --
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
#Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.httpUrl().newBuilder()
.addQueryParameter("platform", "android")
.addQueryParameter("app_version", com.package.BuildConfig.VERSION_NAME)
.build();
Request newRequest = chain.request().newBuilder().url(url).build();
return chain.proceed(newRequest);
}
});
Add the client to your retrofit --
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com")
.client(client)
.build();
With retrofix 2.3.0 the following code can be used:
final OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(final Chain chain) throws IOException
{
final Request request = chain.request();
final HttpUrl url = chain.request().url().newBuilder()
.addQueryParameter("platform", "android")
.addQueryParameter("app_version", com.package.BuildConfig.VERSION_NAME)
.build();
final Request newRequest = chain.request().newBuilder().url(url).build();
return chain.proceed(newRequest);
}
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com")
.client(client)
.build();