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;
}
}
Related
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()
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();
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.
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();
I am using OAuth and I need to put the OAuth token in my header every time I make a request. I see the #Header annotation, but is there a way to make it parameterized so i can pass in at run time?
Here is the concept
#Header({Authorization:'OAuth {var}', api_version={var} })
Can you pass them in at Runtime?
#GET("/users")
void getUsers(
#Header("Authorization") String auth,
#Header("X-Api-Version") String version,
Callback<User> callback
)
Besides using #Header parameter, I'd rather use RequestInterceptor to update all your request without changing your interface. Using something like:
RestAdapter.Builder builder = new RestAdapter.Builder()
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("Accept", "application/json;versions=1");
if (isUserLoggedIn()) {
request.addHeader("Authorization", getToken());
}
}
});
p/s : If you are using Retrofit2, you should use Interceptor instead of RequestInterceptor
Since RequestInterceptor is not longer available in Retrofit 2.0
Yes, you can pass them in runtime. As a matter of fact, pretty much exactly as you typed it out. This would be in your API interface class, named say SecretApiInterface.java
public interface SecretApiInterface {
#GET("/secret_things")
SecretThing.List getSecretThings(#Header("Authorization") String token)
}
Then you pass the parameters to this interface from your request, something along those lines: (this file would be for example SecretThingRequest.java)
public class SecretThingRequest extends RetrofitSpiceRequest<SecretThing.List, SecretApiInteface>{
private String token;
public SecretThingRequest(String token) {
super(SecretThing.List.class, SecretApiInterface.class);
this.token = token;
}
#Override
public SecretThing.List loadDataFromNetwork() {
SecretApiInterface service = getService();
return service.getSecretThings(Somehow.Magically.getToken());
}
}
Where Somehow.Magically.getToken() is a method call that returns a token, it is up to you where and how you define it.
You can of course have more than one #Header("Blah") String blah annotations in the interface implementation, as in your case!
I found it confusing too, the documentation clearly says it replaces the header, but it DOESN'T!
It is in fact added as with #Headers("hardcoded_string_of_liited_use") annotation
Hope this helps ;)
The accepted answer is for an older version of Retrofit. For future viewers the way to do this with Retrofit 2.0 is using a custom OkHttp client:
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Builder ongoing = chain.request().newBuilder();
ongoing.addHeader("Accept", "application/json;versions=1");
if (isUserLoggedIn()) {
ongoing.addHeader("Authorization", getToken());
}
return chain.proceed(ongoing.build());
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
// ... extra config
.client(httpClient)
.build();
Hope it helps someone. :)
Retrofit 2.3.0
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder
.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder newRequest = request.newBuilder().header("Authorization", accessToken);
return chain.proceed(newRequest.build());
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(GithubService.BASE_URL)
.client(okHttpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
I am using this to connect to GitHub.