Adding fields to URL using Retrofit - android

I am building an Android APP where I use the Internet Game Database API through Mashape market place. I am using Retrofit for the get requests and getting data from the API requires an API key.
I got it to work but the API only return game ids and I want the game names and other information, but I am not sure how to add the fields. This is how Mashape query it:
HttpResponse<String> response = Unirest.get("https://igdbcom-internet-game-database-v1.p.mashape.com/games/?fields=name%2Crelease_dates")
.header("X-Mashape-Key", "API KEY HERE")
.header("Accept", "application/json")
.asString();
and this is my Retrofit Interface
public interface GamesAPIService {
#GET("/games/")
Call<List<GamesResponse>> gameList(#Query("mashape-key") String apikey);
}
I tried to use this
#GET("/games/?fields=name,release_dates")
But no luck, I also tried with #Field but didn't work either. Any ideas? Thanks.
Edit: Just to clarify when I add the "?fields=name,release_dates" I get 401 Unauthorized Error.

Firstly I think you need to add mashape key to all your request.
OkHttpClient httpClient = new OkHttpClient();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("X-Mashape-Key", "API_KEY_HERE")
.addHeader("Accept", "application/json")
.build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://igdbcom-internet-game-database-v1.p.mashape.com")
.client(httpClient)
.build();
And then this is information query.
public interface GamesAPIService {
#GET("/games")
Call<List<GamesResponse>> gameList(#Query("fields") String value);
}
And last thing for calling.
GamesAPIService gamesAPIService = retrofit.create(GamesAPIService.class);
Call<List<GamesResponse>> call = gamesAPIService.gameList("name,release_dates");
if (call!=null){
call.enqueue(new Callback<List<GamesResponse>>() {
#Override
public void onResponse(Call<List<GamesResponse>> call, Response<List<GamesResponse>> response) {
// handle success
}
#Override
public void onFailure(Throwable t) {
// handle failure
}
});
}

Related

Retrofit Okhttp send dymanic request headers using interceptor?

I am trying to sent dynamic headers using Okhttp interceptor, but the problem I am facing is when I am calling setretrofitclient() method at the same time, then the header sent for both request are same. This is happening because as okhttp interceptor went in background thread to execute the request but at the same time in UI thread header value gets changed because of which when okhttp send the request it send new updated header.
String header;
public String getHeader() {
return header;
}
static RetrofitInterface retrofitInterface;;
public RetrofitInterface setretrofitclient(String header) {
this.header = header;
if (retrofit != null) {
return retrofit;
} else {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("x-param", getHeader()).build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl(url).client(httpClient.build()).build();
retrofitInterface = retrofit.create(RetrofitInterface.class);
return retrofitInterface;
}
}
Can anyone please suggest me the design pattern how can I make this work for parallel requests.
Thanks.

retrofit , okhttp3 add header

I need to get the XML file from the site. I'm learning to use Retrofit.
I need to make a request and attach my API key via the "X-AppId" header. It should look like this:
X-AppId: my key.
If I do this from the browser, I get the answer.
Through the retrofit I get the access
error 403 Forbidden code = 403, message = Forbidden, url = https: //
Tell me how it is implemented properly to receive an answer from the server code = 200
Here is my implementation:
public interface myAPIinterface {
#GET("/api/ru/index/route/?from=Minsk&to=Warsaw")
Call<Routes> getProducts();
}
This is the activity where I output to the log:
private void getProducts(){
final ProgressDialog loading = ProgressDialog.show(this,"Fetching Data","Please wait...",false,false);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Log.d(TAG, "getProducts");
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("X-AppId:", "97377f7b702d7198e47a2bf12eec74")
.build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://rasp.rw.by")
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();
myAPIinterface api = retrofit.create(myAPIinterface.class);
Call<Routes> call = api.getProducts();
call.enqueue(new Callback<Routes>() {
#Override
public void onResponse(#NonNull Call<Routes> call, #NonNull Response<Routes> response) {
Log.d(TAG, "onResponse");
Log.d(TAG, String.valueOf(kk));
Log.d(TAG, String.valueOf(response));
loading.dismiss();}
#Override
public void onFailure(Call<Routes> call, Throwable throwable) {
loading.dismiss();
Log.d(TAG, "onFailure" + throwable);
}
});
this is a log:
Response{protocol=http/1.1, code=403, message=Forbidden,
url=https://rasp.rw.by/api/ru/index/route/?from=Minsk&to=Warsaw}
if I take third-party sites where there are no headers, I get a response of 200 without problems. What am I doing wrong in this case? Thank you.
Oh, man, what are you doing. You can use annotations like #Query, #Header, etc.
public interface myAPIinterface {
#GET("/api/ru/index/route")
Call<Routes> getProducts(#Header("X-AppId:") String YOUR_APP_ID,
#Query("from") String from,
#Query("to") String to)
}
Then you can create request like this:
Retrofit retrofit = new Retrofit.Builder().
.baseUrl("https://rasp.rw.by")
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();
retrofit.create(myAPIinterface.class).getProducts(myId, "Minsk", "Warsaw").enqueue ...
How It can help? You forgot to add header at second retrofit and then you have 403 error. So, You must add annotations, and this will be the last mistake when you forgot to put value to header/query/etc.

Redundancy requests with Retrofit

I need to build in redundancy into my app where if a server is down it will try a backup redundancy server upon failure of the first request.
Aside from doing
Call<LoginResult> loginCall = apiInterface.login(....);
loginCall.enqueue(new Callback<LoginResult>() {
#Override
public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
if(response.isSuccessful){
//do normal stuff
}else{
//try second url
}
}
#Override
public void onFailure(Call<LoginResult> call, Throwable t) {
//Try second url
}
}
I don't see a clean way to do this. Creating another retrofit request inside the error block or non-successful block would add a lot of code complexity.
Is there an easier way to handle this in Retrofit or OkHttp?
I have here an option with OkHttp interceptors. The idea is that if the request fails you replace the url and execute the request again.
The following is an api client to the OpenWeather Api. If you want to try out the example you'll need to sign up and get an api key. It should be free so I hope this is ok.
I'll post here the full code and then walk you through it.
private final static String API_KEY = "<API KEY HERE>";
private static class Weather {
#SerializedName("id")
#Expose
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
private static final String GOOD_HOST = "api.openweathermap.org";
private static final String BAD_ENDPOINT = "https://api.aaaaaaaaaaa.org";
interface WeatherApiClient {
#GET("/data/2.5/weather")
Call<Weather> get(
#Query("q") String query,
#Query("appid") String apiKey);
}
private static class ReplicaServerInterceptor implements Interceptor {
#Override public okhttp3.Response intercept(Chain chain)
throws IOException {
try {
okhttp3.Response response = chain.proceed(chain.request());
return response;
} catch (IOException e) {
// Let's build a new request based on the old one
Request failedRequest = chain.request();
HttpUrl replicaUrl = failedRequest.url()
.newBuilder()
.host(GOOD_HOST)
.build();
okhttp3.Request request = failedRequest.newBuilder()
.url(replicaUrl)
.build();
return chain.proceed(request);
}
}
}
public static void main(String[] args) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new ReplicaServerInterceptor())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BAD_ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
WeatherApiClient weatherApiClient =
retrofit.create(WeatherApiClient.class);
weatherApiClient.get("Lisbon,pt", API_KEY)
.enqueue(new Callback<Weather>() {
#Override public void onResponse(
Call<Weather> call,
Response<Weather> response) {
// This might be null sometimes because
// the api is not super reliable, but I didn't
// add code for this
System.out.println(response.body().id);
}
#Override public void onFailure(
Call<Weather> call,
Throwable t) {
t.printStackTrace();
}
});
}
To be able to fake a server failure I prepare retrofit to call a non existent url - BAD_ENDPOINT. This will trigger the catch clause inside the interceptor.
The interceptor itself is obviously the key thing here. It intercepts every call from retrofit and executes the call. If the call throws an error because the server is down, then it will raise an IOException. Here I copy the request being made and change the url.
Changing the url means changing the host:
HttpUrl replicaUrl = failedRequest.url()
.newBuilder()
.host(GOOD_HOST)
.build();
If you just call url(<some url>) in the request builder, everything gets replaced. Query parameters, protocol, etc. This way, we preserve these from the original request.
(OkHttp offers newBuilder methods which copy the data from the current object and let you just edit what you want. Just like kotlin's copy. This is why we can simply change the url and be safe that everything else remains the same)
I then build the new request with the url and execute it:
okhttp3.Request request = failedRequest.newBuilder()
.url(replicaUrl)
.build();
return chain.proceed(request);
Interceptors work on a chain pattern, that's why calling proceed will call the next interceptor on the chain. In this case we just need to actually make the request.
I didn't bother copying the entire weather resource, so I'm just using the id. I think that's not the main focus of the question
As I said before, this is meant as a proof of concept. As you noticed I'm try-catching the execution of the call, but in your case it might be that the call actually succeeds executing, but the http response is not a 2XX. The okhttp response objects have methods that help you checking if the response was successful namely - isSuccessful(). The idea is the same - Build a new request and carry on if it's not successful.
I didn't bother treating any errors from the replica in this example. They'll just be forwarded to the retrofit client.
As you can see retrofit has no clue where the response is coming from. This might or not be good. Also, the response body needs to be the same from both servers, which I guess it's the case.
Lastly I'm sorry for the awkward okhttp3.Response name spacing there. I was using both Response from retrofit and okhttp and hence had to avoid the name clash.
Versions used for this example: Retrofit 2.3.0 and the okhttp bundled with that

OAuth issue of request from Android app with Retrofit & Fabric

I am developing Android App interacting with Twitter using Fabric and Retrofit2 libraries. I want to display search timeline. My request URL is like this: https://api.twitter.com/1.1/friends/list.json?screen_name=xxx
The response body I got is null but I got the alert of bad authentication:215 and http error 400 in the debug mode.This is probably caused by invalid authentication of the request from my app.
The Twitter developer document said requests need to be authorized with OAuth and SSL certificated.
As for the OAuth issue, I wrote the request header based on the official document of twitter developer platform https://dev.twitter.com/oauth/overview/authorizing-requests
and create the header with okhttpclient and pass it to retrofit object.
The code for OAuth issue is like this.
public class TwitterClientApiClient extends TwitterApiClient {
private static final String TAG=TwitterClientApiClient.class.getSimpleName();
private static final MainApplication app=MainApplication.getInstance();
public static final String BASE_URL = "https://api.twitter.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
final String authStr = app.authStr();
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("Accept", "application/json")
.header("Authorization", authStr)
.method(original.method(), original.body())
.build();
Headers okHeaders = request.headers();
Log.d(TAG,okHeaders.toString());
return chain.proceed(request);
}
});
OkHttpClient client = httpClient.build();
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
return retrofit;
}
public TwitterClientApiClient(TwitterSession session) {
super(session);
}
public FriendsService getFriendsService() {return getService(FriendsService.class);}
}
interface FriendsService {
#GET("/1.1/friends/list.json")
Call<FriendsResult> list(#Query("screen_name") String screen_name);
}
The following is the code making the request.
FriendsService apiService =
TwitterClientApiClient.getClient().create(FriendsService.class);
Call<FriendsResult> call = apiService.list(screenName);
Log.d(TAG, call.request().url().toString());
call.enqueue(new Callback<FriendsResult>() {
#Override
public void onResponse(Call<FriendsResult> call, Response<FriendsResult> response) {
//List<User> friends = response.body().getUsers();
Log.d(TAG,response.body().toString());
//Log.d(TAG, "Number of Friends: " + friends.size());
//String q = getQueryStr(friends);
//showSearchedTimeline(q);
}
#Override
public void onFailure(Call<FriendsResult>call, Throwable t) {
Log.e(TAG, t.toString());
}
});
However,according to https://oauth.net/core/1.0/#encoding_parameters
OAuth Authentication is done in three steps:
1.The Consumer obtains an unauthorized Request Token.
2.The User authorizes the Request Token.
3.The Consumer exchanges the Request Token for an Access Token.
My code which is based on references from the internet seems to do only Step 3 and thus the authentication is not complete. I wonder how to complete the whole authentication process of OAuth.
Also do I need to do sth in my code for SSL stuff?
Besides OAuth and SSL, any other security issue for request to twitter server I have overlooked?
Thanks in advance!
.header("Authorization", authStr)
Try with addHeader. You can activate the logs (useful to debug sometimes) using a logging interceptor. Ask the logger to show your headers, to see if that could be the problem. Available levels are here.

How to get headers from all responses using retrofit

I'm using Retrofit library version 2 with OkHttpClient.
I want to get some header from all responses.
I found one solution with OkClient:
public class InterceptingOkClient extends OkClient{
public InterceptingOkClient()
{
}
public InterceptingOkClient(OkHttpClient client)
{
super(client);
}
#Override
public Response execute(Request request) throws IOException
{
Response response = super.execute(request);
for (Header header : response.getHeaders())
{
// do something with header
}
return response;
}
}
But how i can do this if i'm using OkHttpClient?
Yes, this is old question.. but still found to answer because myself too was searching similar one.
okHttpClient.interceptors().add(new Interceptor() {
#Override
public Response intercept(Interceptor.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, to add new header - replaces value with same header name.
Request request = requestBuilder.build();
Response response = chain.proceed(request);
Headers allHeaders = response.headers();
String headerValue = allHeaders.get("headerName");
return response;
}
});
Hope, this helps!
P.S: no error handled.
You can use the logging interceptor for that. Add it as an interceptor to your OkHttpClient builder while building the client, set the log level and voila! You will have all the information regarding the request as well as the response.
Here's how you can add the interceptor -
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpBuilder.addInterceptor(loggingInterceptor);
client = okHttpBuilder.build();
There are four options when it comes to what you want to Log - NONE,BASIC,HEADERS, and BODY.
Now build the the retrofit instance with the above defined client and you will have all the data you need.

Categories

Resources