Retrofit default query parameter [duplicate] - java

This question already has answers here:
Is there a way to add query parameter to every request with Retrofit 2?
(7 answers)
Closed 1 year ago.
I have an apikey "token" that has to be provided as a query parameter in every request.
How can i setup retrofit as to always include this query parameter in the request?
I am currently providing the token as a default parameter:
interface CompanyService {
#GET("accounts/{id}")
fun getCompany(
#Path("id") id: Number,
#Query("token") token: String = Constants.TOKEN) <---- here
: Call<CompanyResponse>
#GET("accounts/")
fun getCompanies(
#Query("id") page: String,
#Query("limit") limit: Int,
#Query("sort") sort: String = "id",
#Query("token") token: String = Constants.TOKEN) <---- here
: Call<CompanyListResponse>
}
But since i know that every request requires the same token, it feels redundant to have to provide it as a default parameter in every request function i create. How can i setup retrofit to decorate every outgoing request with a default query parameter?
This is how i build the retrofit2 instance:
class CompanyAPI {
companion object {
private var retrofit: Retrofit? = null
val client:Retrofit get() {
if (retrofit == null) {
retrofit=Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(Constants.BASE_URL)
.build()
}
return retrofit!!
}
}
}
I would think that you would add some additional function call in the call chain before calling .build() but looking at the available functions i can't say i see anything that would provide this functionality.

add interceptor to your http client with your default params or headers here is example for how to add it :-
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain)
throws IOException {
Request original = chain.request();
HttpUrl originalHttpUrl = original.url();
HttpUrl url = originalHttpUrl.newBuilder()
.addQueryParameter("apikey", "your-actual-api-key")
.build();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.url(url);
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
refrence

You need to add an interceptor to it & pass the client in your Builder like the following:
retrofit=Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(Constants.BASE_URL)
.client(
OkHttpClient.Builder()
.addInterceptor { chain ->
val url = chain
.request()
.url()
.newBuilder()
.addQueryParameter("token", Constants.TOKEN)
.build()
chain.proceed(chain.request().newBuilder().url(url).build())
}
.build()
)
.build()

Related

why is my retrofit call is returning an unsuccessful response?

so basically am using retrofit to get data from an api called calorieNinja and for some reason i keep getting an unsuccessful response
here is the retrofit code :
retrofit = new Retrofit.Builder().baseUrl("https://api.calorieninjas.com/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiCalorieNinjas apiCalorieNinjas = retrofit.create(ApiCalorieNinjas.class);
Call<MealCalories> call = apiCalorieNinjas.getMeal("nutrition?query= 5 eggs");
call.enqueue(new Callback<MealCalories>() {
#Override
public void onResponse(Call<MealCalories> call, Response<MealCalories> response) {
if(!response.isSuccessful()){
Toast.makeText(getContext(),"Not Found",Toast.LENGTH_SHORT).show();
return;
}
mealEaten = response.body();
Meal meal1 = new Meal(mealEaten.getName(),mealEaten.getCalories(),mealEaten.getProtein_g(),mealEaten.getCarbohydrates_total_g());
mealViewModel.insertMeal(meal1);
}
#Override
public void onFailure(Call<MealCalories> call, Throwable t) {
}
});
}
});
btw am using 2 different types of meal objects because one is responsible of getting the data from the api and one is used as an entity for Room databse and they dont have the same parameters so instead of just adding #Ignore i decided to use two different objects while i try fixing this problem.
and here is the interface of it :
public interface ApiCalorieNinjas {
#Headers("X-Api-Key: PiQfBb0CZy2GfOZWjWyj6Tg==EdGjoESjqxQh1q4M")
#GET("{meal}")
public Call<MealCalories> getMeal(#Path("meal") String meal);
the api key isnt real!
if additional code is needed please let me know!
Try to add an interceptor so you can see all calls logs (headers, body, URLs, etc...) and see what it's the error that the API is sending.
Add OkHtpp to your grade dependencies:
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.2"
implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2"
And after that, when you create your Retrofit instance, add the interceptor, should look something like this:
val httpClient = OkHttpClient.Builder()
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
httpClient.addInterceptor(interceptor)
httpClient.addInterceptor(Interceptor { chain: Interceptor.Chain ->
val original: Request = chain.request()
val request: Request = original.newBuilder()
.header("Content-Type", "application/json")
.method(original.method, original.body)
.build()
chain.proceed(request)
})
val okHttpClient = httpClient.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.calorieninjas.com/v1/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()

Is it possible to pass a string to a interface? Or at lest fetch from SharedPreferences inside a interface?

I'm trying to fetch a stored JWT from the SharedPreferences so I can send them as a Header in my requrest but I'm not able to get that data inside the API interface. Is this possible?
Thanks
SOLVED:
For anyone looking for this: you can pass a Header as a param, ex.:
#FormUrlEncoded
#POST("users/getUser")
Call<String> getUser(
#Header("Token") String token,
#Field("user") String user
);
also you can use an integrated Interceptor to adding this token on ALL requests like this:
public class AuthInterceptor
implements Interceptor {
#Override
public Response intercept(Chain chain)
throws IOException {
Request request = chain.request();
request = request.newBuilder()
.addHeader("Token", new MySharedPref().getToken())
.build();
return chain.proceed(request);
}
}
and after that add an instance of it on your OkHttpClient :
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addNetworkInterceptor(new AuthInterceptor());
Retrofit retrofit = new Retrofit.Builder()
...
.client(httpClient.build())
.build();

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.

Adding header to all request with Retrofit 2

Retrofit 2's documentation says:
Headers that need to be added to every request can be specified using an OkHttp interceptor.
It can be done easily using the previous version, here's the related QA.
But using retrofit 2, I couldn't find something like setRequestInterceptor or setInterceptor method that can be applied to Retrofit.Builder object.
Also it seems that there's no RequestInterceptor in OkHttp anymore. Retrofit's doc refers us to Interceptor that I didn't quite understand how to use it for this purpose.
How can I do this?
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("parameter", "value").build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl(url).client(httpClient.build()).build();
The Latest Retrofit Version HERE -> 2.1.0.
lambda version:
builder.addInterceptor(chain -> {
Request request = chain.request().newBuilder().addHeader("key", "value").build();
return chain.proceed(request);
});
ugly long version:
builder.addInterceptor(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("key", "value").build();
return chain.proceed(request);
}
});
full version:
class Factory {
public static APIService create(Context context) {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(10, TimeUnit.SECONDS);
builder.connectTimeout(5, TimeUnit.SECONDS);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addInterceptor(interceptor);
}
builder.addInterceptor(chain -> {
Request request = chain.request().newBuilder().addHeader("key", "value").build();
return chain.proceed(request);
});
builder.addInterceptor(new UnauthorisedInterceptor(context));
OkHttpClient client = builder.build();
Retrofit retrofit =
new Retrofit.Builder().baseUrl(APIService.ENDPOINT).client(client).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
return retrofit.create(APIService.class);
}
}
gradle file (you need to add the logging interceptor if you plan to use it):
//----- Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile "com.squareup.retrofit2:converter-gson:2.1.0"
compile "com.squareup.retrofit2:adapter-rxjava:2.1.0"
compile 'com.squareup.okhttp3:logging-interceptor:3.4.0'
Try this type header for Retrofit 1.9 and 2.0. For Json Content Type.
#Headers({"Accept: application/json"})
#POST("user/classes")
Call<playlist> addToPlaylist(#Body PlaylistParm parm);
You can add many more headers i.e
#Headers({
"Accept: application/json",
"User-Agent: Your-App-Name",
"Cache-Control: max-age=640000"
})
Dynamically Add to headers:
#POST("user/classes")
Call<ResponseModel> addToPlaylist(#Header("Content-Type") String content_type, #Body RequestModel req);
Call you method i.e
mAPI.addToPlayList("application/json", playListParam);
Or
Want to pass everytime then Create HttpClient object with http Interceptor:
OkHttpClient httpClient = new OkHttpClient();
httpClient.networkInterceptors().add(new Interceptor() {
#Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
Request.Builder requestBuilder = chain.request().newBuilder();
requestBuilder.header("Content-Type", "application/json");
return chain.proceed(requestBuilder.build());
}
});
Then add to retrofit object
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).client(httpClient).build();
UPDATE if you are using Kotlin remove the { } else it will not work
For Logging your request and response you need an interceptor and also for setting the header you need an interceptor, Here's the solution for adding both the interceptor at once using retrofit 2.1
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();
In my case addInterceptor()didn't work to add HTTP headers to my request, I had to use addNetworkInterceptor(). Code is as follows:
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addNetworkInterceptor(new AddHeaderInterceptor());
And the interceptor code:
public class AddHeaderInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
builder.addHeader("Authorization", "MyauthHeaderContent");
return chain.proceed(builder.build());
}
}
This and more examples on this gist
If you use addInterceptor method for add HttpLoggingInterceptor, it won't be logging the things that added by other interceptors applied later than HttpLoggingInterceptor.
For example: If you have two interceptors "HttpLoggingInterceptor" and "AuthInterceptor", and HttpLoggingInterceptor applied first, then you
can't view the http-params or headers which set by AuthInterceptor.
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addNetworkInterceptor(logging)
.addInterceptor(new AuthInterceptor());
I solved it, via using addNetworkInterceptor method.
In kotlin adding interceptor looks that way:
.addInterceptor{ it.proceed(it.request().newBuilder().addHeader("Cache-Control", "no-store").build())}
Use this Retrofit Client
class RetrofitClient2(context: Context) : OkHttpClient() {
private var mContext:Context = context
private var retrofit: Retrofit? = null
val client: Retrofit?
get() {
val logging = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient.Builder()
.connectTimeout(Constants.TIME_OUT, TimeUnit.SECONDS)
.readTimeout(Constants.TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(Constants.TIME_OUT, TimeUnit.SECONDS)
client.addInterceptor(logging)
client.interceptors().add(AddCookiesInterceptor(mContext))
val gson = GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create()
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(Constants.URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client.build())
.build()
}
return retrofit
}
}
I'm passing the JWT along with every request. Please don't mind the variable names, it's a bit confusing.
class AddCookiesInterceptor(context: Context) : Interceptor {
val mContext: Context = context
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
val preferences = CookieStore().getCookies(mContext)
if (preferences != null) {
for (cookie in preferences!!) {
builder.addHeader("Authorization", cookie)
}
}
return chain.proceed(builder.build())
}
}
RetrofitHelper library written in kotlin, will let you make API calls, using a few lines of code.
Add headers in your application class like this :
class Application : Application() {
override fun onCreate() {
super.onCreate()
retrofitClient = RetrofitClient.instance
//api url
.setBaseUrl("https://reqres.in/")
//you can set multiple urls
// .setUrl("example","http://ngrok.io/api/")
//set timeouts
.setConnectionTimeout(4)
.setReadingTimeout(15)
//enable cache
.enableCaching(this)
//add Headers
.addHeader("Content-Type", "application/json")
.addHeader("client", "android")
.addHeader("language", Locale.getDefault().language)
.addHeader("os", android.os.Build.VERSION.RELEASE)
}
companion object {
lateinit var retrofitClient: RetrofitClient
}
}
And then make your call:
retrofitClient.Get<GetResponseModel>()
//set path
.setPath("api/users/2")
//set url params Key-Value or HashMap
.setUrlParams("KEY","Value")
// you can add header here
.addHeaders("key","value")
.setResponseHandler(GetResponseModel::class.java,
object : ResponseHandler<GetResponseModel>() {
override fun onSuccess(response: Response<GetResponseModel>) {
super.onSuccess(response)
//handle response
}
}).run(this)
For more information see the documentation
Kotlin version would be
fun getHeaderInterceptor():Interceptor{
return object : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request =
chain.request().newBuilder()
.header(Headers.KEY_AUTHORIZATION, "Bearer.....")
.build()
return chain.proceed(request)
}
}
}
private fun createOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.apply {
if(BuildConfig.DEBUG){
this.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))
}
}
.addInterceptor(getHeaderInterceptor())
.build()
}

Retrofit get a parameter from a redirect URL

I am using Retrofit.
I have an endpoint that redirects to another endpoint. The latter (the endpoint that I end up at) has a parameter in its URL that I need. What is the best way to get the value of this parameter?
I cannot even figure out how to get the URL that I am redirected to, using Retrofit.
OkHttp's Response will give you the wire-level request (https://square.github.io/okhttp/3.x/okhttp/okhttp3/Response.html#request--). This will be the Request that initiated the Response from the redirect. The Request will give you its HttpUrl, and HttpUrl can give you its parameters' keys and values, paths, etc.
With Retrofit 2, simply use retrofit2.Response.raw() to get the okhttp3.Response and follow the above.
I am using retrofit. And I can get the redirect url following this way :
private boolean handleRedirectUrl(RetrofitError cause) {
if (cause != null && cause.getResponse() != null) {
List<Header> headers = cause.getResponse().getHeaders();
for (Header header : headers) {
//KEY_HEADER_REDIRECT_LOCATION = "Location"
if (KEY_HEADER_REDIRECT_LOCATION.equals(header.getName())) {
String redirectUrl = header.getValue();
return true;
}
}
}
return false;
}
Hope it could help someone.
Solution for this would be to use an interceptor e.g.
private Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response response = chain.proceed(chain.request());
locationHistory.add(response.header("Location"));
return response;
}
};
Add the interceptor to your HttpClient and add that to Retrofit(using 2.0 for this example)
public void request(String url) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.followRedirects(true);
client.addNetworkInterceptor(interceptor);
OkHttpClient httpClient = client.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
}
Now you have full access the the entire redirect history.

Categories