retrofit2: adding RequestInterceptor - java

Guys please am trying to upgrade from retrofit 1 to retrofit 2 but am having a hard time adding a request interceptor for all requests.
In retrofit 1, I achieved it by doing something like this;
public static <S> S createService(Class<S> serviceClass, final String token) {
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(Constant.APP_URL);
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("token", token);
}
});
RestAdapter adapter = builder.build();
return adapter.create(serviceClass);
}
But i just cant get my head around this in retrofit 2... Please does anyone have a solution to my problem?

In your dependencies block add:
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
In your Retrofit service class add:
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
then add:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.client(client) // this is the line you care about
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(JacksonConverterFactory.create())
.build();

Related

I want to work with multiple base url in retrofit2

I have a structure whose base url address is constantly variable. When my user changes his location in the application, I change the base url to show the closest server to him. but the retrofit client does not regenerate even though I restart the activities and change all the static variables. The only way to do this is to close the application completely and restart it.
I don't have a fixed base url. There could be 4 different locations today, 12 different locations tomorrow. so i need to find a permanent solution.
I have examined a lot in debug mode, but; When the base url changes, it never goes inside the getClient and cannot reassign the base url.
public static Retrofit retrofit = null;
// I want this address to change.
public static String baseUrl = "";
#Provides
public static Retrofit getClient() {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Date(json.getAsJsonPrimitive().getAsLong());
}
});
Gson gson = builder.create();
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("AUTHORIZATION", "" + JWT_TOKEN)
.build();
return chain.proceed(request);
}
}).build();
if (retrofit == null) {
retrofit = new Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build();
return retrofit;
}
return retrofit;
}
You can use a dynamic URL in your application during runtime. Retrofit2 introduced the #Url annotation that allows us to pass a complete URL for an endpoint:
#GET
public Call<ResponseBody> profilePicture(#Url String url);
EDITED
You should create a new Retrofit instance when you want to use a new base URL.
public class ServiceGenerator {
public static String apiBaseUrl = "http://futurestud.io/api";
private static Retrofit retrofit;
private static Retrofit.Builder builder =
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(apiBaseUrl);
private static OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
// No need to instantiate this class.
private ServiceGenerator() {
}
public static void changeApiBaseUrl(String newApiBaseUrl) {
apiBaseUrl = newApiBaseUrl;
builder = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(apiBaseUrl);
}
public static <S> S createService(Class<S> serviceClass, AccessToken token) {
String authToken = token.getTokenType().concat(token.getAccessToken());
return createService(serviceClass, authToken);
}
// more methods
// ...
}
https://futurestud.io/tutorials/retrofit-2-how-to-change-api-base-url-at-runtime-2
I used SharedPreferences to handle this problem:
#Singleton
#Provides
suspend fun getBaseUrl(preferencesManager: PreferencesManager): String {
return preferencesManager.getPrefBaseUrl()
}
#Singleton
#Provides
fun provideGsonBuilder(): Gson {
return GsonBuilder()
.setLenient()
.create()
}
#Provides
#Singleton
fun provideOkHttpClient( ) =
OkHttpClient
.Builder()
.connectTimeout(3, TimeUnit.MINUTES)
.readTimeout(3, TimeUnit.MINUTES)
.writeTimeout(3, TimeUnit.MINUTES)
.build()
#Singleton
#Provides
fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient, preferencesManager: PreferencesManager): Retrofit.Builder {
return Retrofit.Builder()
.baseUrl( preferencesManager.getPrefBaseUrl())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
}
#Singleton
#Provides
fun provideBlogService(retrofit: Retrofit.Builder): MyApi {
return retrofit
.build()
.create(MyApi::class.java)
}

Retrofit BaseUrl And API

I am learnng how to use retrofit library,however I come across a little problem.
So I set everything and run my project but first I git this error:
Caused by: java.lang.IllegalArgumentException: baseUrl must end in /:
So I added the "/" but then I realized that its more than that, and I should leave just the baseUrl and add the api to the interface I created.
I tried to add the api in diffrent ways but I didn't manage to do it.
Here are some codes:
Retrofit BaseUrl:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.nytimes.com/svc/movies/v2/critics/full-time.json?api-key=abcdefghijklmnop")
.addConverterFactory(GsonConverterFactory.create())
.build();
At first I used it like above,but after some research I discovered that I have to leave the baseUrl and pass the other half of the address through the interface.
Interface -
public interface ConnectAPI {
#GET("results")
Call<List<Reviewers>> getReviewers();
}
I will be glad for some help,
Thanks !
As Gabe Sechan said, when working with Retrofit you need to set a base url that will be the same for all api calls and then append the rest of the url on a per endpoint basis. If you change your code to the below you should be good
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.nytimes.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
and getReviewers() to
public interface ConnectAPI {
#GET("svc/movies/v2/critics/full-time.json?api-key=abcdefghijklmnop")
Call<List<Reviewers>> getReviewers();
}
Base URL should be the root of all the queries you want to make on that interface. The rest should be part of the URL on the actual API interface. So for your example, base URL should probably be "https://api.nytimes.com/svc/movies/v2/". Although any subset of that, such as "https://api.nytimes.com/" would also work as long as the interface has all the rest of the path. Basically when the actual HTTP request is made, the URL of the query is concatenated to the end of the base url.
//ApiClient class for BaseUrl(Retrofit)
public class ApiClient {
private static String BASE_URL="";
private static OkHttpClient getOkHttpClient(){
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging).addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
HttpUrl originalHttpUrl = original.url();
HttpUrl url = originalHttpUrl.newBuilder().build();
Request.Builder requestBuilder = chain.request().newBuilder()
.addHeader("Content-Type","application/json")
.addHeader("timezone", TimeZone.getDefault().getID())
.url(url);
Request request = requestBuilder.build();
Response response = chain.proceed(request);
return response;
}
});
return httpClient.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
}
private static Retrofit.Builder retrofitBuilder=new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(getOkHttpClient())
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit=retrofitBuilder.build();
private static ApiService apiService=retrofit.create(ApiService.class);
public static ApiService getApiService(){
return apiService;
}
}

Can not stop retrofit sending

I use retrofit and OkHttp3 library to send some messages to a server and set its as below :
okClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15,TimeUnit.SECONDS)
.addInterceptor(interceptor)
.build();
When I want to send a large message(which, for example, it takes about 2 minutes), Retrofit sends my file completely, and after 2 minutes, I get the TimeOut message. If I expect to stop sending after 15 seconds and show me the Error message.
Is there a specific item that I must comply with? Please guide me.
Or suggest me a standard way to break this operation after 15 second.
mycode:
class RetrofitFactory {
private static final RetrofitFactory INSTANCE = new RetrofitFactory();
public static RetrofitFactory getInstance() {
return INSTANCE;
}
public OkHttpClient getOkHttp()
{
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15,TimeUnit.SECONDS)
.addInterceptor(new GzipRequestInterceptor())
.addInterceptor(interceptor)
.build();
return okClient;
}
public myInterface getlimit()
{
if (retrofit == null) {
OkHttpClient okClient = getOkHttp();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
retrofit = new Retrofit.Builder()
.client(okClient)
.baseUrl(BuildConfig.BASEURL)
.addConverterFactory(JacksonConverterFactory.create(objectMapper))
.build();
}
return retrofit.create(myInterface.class);
}
}
public interface myInterface{
#POST("api/ReadingApi/Something")
Call<Something> DoReading(
#Body List<Something> list,
#Header("Authorization") String auth);
}
Call<DoReadResult> x = RetrofitFactory.getInstance().getlimit().DoReading(
data, "Something");
response = x.execute();
Update:
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-jackson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
As you said you are using retrofit , so you need to cancel your call easily with retrofit Call :
Call<ResponseBody> call =
uploadService.uploadSomething(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, "request success");
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, "request failed");
}
});
}
call.cancel();
with call.cancel(); you can cancel your request.
See more here :
Retrofit Cancel Request

Dagger + Retrofit. Adding auth headers at runtime

I'm wondering if there is a way for Dagger to know that it should recreate an object when new data is available.
The instance I am speaking of is with the request headers I have for retrofit. At some point (when the user logs in) I get a token that I need to add to the headers of retrofit to make authenticated requests. The issue is, I'm left with the same unauthenticated version of retrofit. Here's my injection code:
#Provides
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.cache(cache).build();
client
.newBuilder()
.addInterceptor(
chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Accept", "Application/JSON");
Request request = requestBuilder.build();
return chain.proceed(request);
}).build();
return client;
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
return retrofit;
}
#Provides
#Singleton
public NetworkService providesNetworkService(Retrofit retrofit) {
return retrofit.create(NetworkService.class);
}
Any ideas on how to make this work?
I personally created an okhttp3.Interceptor that does that for me, which I update once I have the required token. It looks something like:
#Singleton
public class MyServiceInterceptor implements Interceptor {
private String sessionToken;
#Inject public MyServiceInterceptor() {
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder requestBuilder = request.newBuilder();
if (request.header(NO_AUTH_HEADER_KEY) == null) {
// needs credentials
if (sessionToken == null) {
throw new RuntimeException("Session token should be defined for auth apis");
} else {
requestBuilder.addHeader("Cookie", sessionToken);
}
}
return chain.proceed(requestBuilder.build());
}
}
In the corresponding dagger component, I expose this interceptor so I can set the sessionToken when I need to.
That is some stuff that Jake talked about it his talk Making Retrofit Work For You.
Please consider using the approach mentioned by #oldergod as it is the "official" and much better way, whereas the approaches mentioned below are not advised, they may be considered as workarounds.
You have a couple of options.
As soon as you get the token, you have to null out the component that provided you the Retrofit instance, create a new component and ask for a new Retrofit instance, which will be instantiated with necessary okhttp instance.
A fast and bad one - Save the token in SharedPreferences, create okHttp header, which will apply token reading from SharedPreferences. If there is none - send no token header.
Even uglier solution - declare a static volatile String field, and do the same thing like in step 2.
Why the second option is bad? Because on each request you would be polling disk and fetch data from there.
Created custom RequestInterceptor with #Inject constructor
RequestInterceptor
#Singleton
class
RequestInterceptor #Inject constructor(
private val preferencesHelper: PreferencesHelper,
) : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var newRequest: Request = chain.request()
newRequest = newRequest.newBuilder()
.addHeader(
"AccessToken",
preferencesHelper.getAccessTokenFromPreference()
)
.build()
Log.d(
"OkHttp", String.format(
"--> Sending request %s on %s%n%s",
newRequest.url(),
chain.connection(),
newRequest.headers()
)
);
return chain.proceed(newRequest)
}
ApplicationModule
#Module(includes = [AppUtilityModule::class])
class ApplicationModule(private val application: AppController) {
#Provides
#Singleton
fun provideApplicationContext(): Context = application
#Singleton
#Provides
fun provideSharedPreferences(): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(application.applicationContext)
}
PreferencesHelper
#Singleton
class PreferencesHelper
#Inject constructor(
private val context: Context,
private val sharedPreferences: SharedPreferences
) {
private val PREF_KEY_ACCESS_TOKEN = "PREF_KEY_ACCESS_TOKEN"
fun getAccessTokenFromPreference(): String? {
return sharedPreferences.getString(PREF_KEY_ACCESS_TOKEN, null)
}
}
Well tested and working
public OkHttpClient getHttpClient(Context context) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.callTimeout(60,TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.addInterceptor(logging)
.addInterceptor(chain -> {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + Utility.getSharedPreferencesString(context, API.AUTHORIZATION))
.build();
return chain.proceed(newRequest);
})
.build();
}
Earlier I was wondering, if session expires and user login again, will this interceptor replace the existing auth, but fortunately it is working fine.

How to define a Header to all request using Retrofit?

I'm looking for a solution to define a unique Header to use in all requests. Today I use #Header to each request did pass like parameter but I want define only header that works in all requests without to need pass like a parameter, for example fixing this Header on my requests #GET and #POST
Today I use this. Note that each request #GET I need define Header as parameter.
//interface
#GET("/json.php")
void getUsuarioLogin(
#Header("Authorization") String token,
#QueryMap Map<String, String> params,
Callback<JsonElement> response
);
//interface
#GET("/json.php")
void addUsuario(
#Header("Authorization") String token,
#QueryMap Map<String, String> params,
Callback<JsonElement> response
);
//using
public void getUsuarioLogin(){
Map<String, String> params = new HashMap<String, String>();
params.put("email", "me#mydomain.com");
params.put("senha", ConvertStringToMD5.getMD5("mypassword"));
RestAdapter adapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(WebServiceURL.getBaseWebServiceURL())
.build();
UsuarioListener listener = adapter.create(UsuarioListener.class);
listener.getUsuarioLogin(
//header
"Basic " + BasicAuthenticationRest.getBasicAuthentication(),
params,
new Callback<JsonElement>() {
#Override
public void success(JsonElement arg0, Response arg1) {
Log.i("Usuario:", arg0.toString() + "");
}
#Override
public void failure(RetrofitError arg0) {
Log.e("ERROR:", arg0.getLocalizedMessage());
}
});
}
//using
public void addUsuario(){
Map<String, String> params = new HashMap<String, String>();
params.put("name", "Fernando");
params.put("lastName", "Paiva");
RestAdapter adapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(WebServiceURL.getBaseWebServiceURL())
.build();
UsuarioListener listener = adapter.create(UsuarioListener.class);
listener.addUsuario(
//header
"Basic " + BasicAuthenticationRest.getBasicAuthentication(),
params,
new Callback<JsonElement>() {
#Override
public void success(JsonElement arg0, Response arg1) {
Log.i("Usuario:", arg0.toString() + "");
}
#Override
public void failure(RetrofitError arg0) {
Log.e("ERROR:", arg0.getLocalizedMessage());
}
});
}
Official document:
Headers that need to be added to every request can be specified using a RequestInterceptor. The following code creates a RequestInterceptor that will add a User-Agent header to every request.
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("User-Agent", "Retrofit-Sample-App");
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setRequestInterceptor(requestInterceptor)
.build();
In Retrofit 2, you need to intercept the request on the network layer provided by OkHttp
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();
Check this, it explains the differences very well.
Depending on your OkHttp lib:
OkHttpClient httpClient = new OkHttpClient();
httpClient.networkInterceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("User-Agent", System.getProperty("http.agent")).build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
As the other answers have described, you need a RequestInterceptor. Luckily, this interface has a single method, so Java 8 and above will treat it as a functional interface and let you implement it with a lambda. Simple!
For example, if you're wrapping a specific API and need a header for each endpoint, you might do this when you build your adapter:
RestAdapter whatever = new RestAdapter.Builder().setEndpoint(endpoint)
.setRequestInterceptor(r -> r.addHeader("X-Special-Vendor-Header", "2.0.0"))
.build()
Here's the solution for adding header using retrofit 2.1. We need to add interceptor
public OkHttpClient getHeader(final String authorizationValue ) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.addNetworkInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = null;
if (authorizationValue != null) {
Log.d("--Authorization-- ", authorizationValue);
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Authorization", authorizationValue);
request = requestBuilder.build();
}
return chain.proceed(request);
}
})
.build();
return okClient;
}
Now in your retrofit object add this header in the client
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.client(getHeader(authorizationValue))
.addConverterFactory(GsonConverterFactory.create())
.build();

Categories