retrofit , okhttp3 add header - android

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.

Related

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

Retrofit2 Code 400 Bad Request

I'm trying to do a POST, but its returning me a error :
com.google.gson.stream.MalformedJsonException: Use
JsonReader.setLenient(true) to accept malformed JSON at line 1 column
1 path $
My Call:
#POST("BuscaPontos")
Call<PontuacaoModel> postPontuacao(#Body PontuacaoModel model);
And my Webservice consum:
try
{
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL_BUSCAR_CIDADE)
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.build();
PontuacaoModel model = new PontuacaoModel();
model.setNome("Juina");
model.setEstado("Mato Grosso");
CallService.Pontuacao callService = retrofit.create(CallService.Pontuacao.class);
Call<PontuacaoModel> requestService = callService.postPontuacao(model);
requestService.enqueue(new Callback<PontuacaoModel>() {
#Override
public void onResponse(Call<PontuacaoModel> call, Response<PontuacaoModel> response) {
if(response.isSuccessful())
{
String i = response.message().toString();
}
}
#Override
public void onFailure(Call<PontuacaoModel> call, Throwable t) {
String i = t.toString();
}
});
}
catch (Exception ex)
{
}
Whats is wrong ?
I don't see a line in your code where you add client during initializing retrofit:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL_BUSCAR_CIDADE)
.client() // add a client instance here, e.g. OkHttpClient
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.build();
This is the issue with your response from server, that may not be correct format. Please install a tool called postman form here.
Use this tool to check whether the response is correct before do some coding.

Adding fields to URL using Retrofit

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
}
});
}

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.

Receiving response body in Retrofit2 but onResponse is not getting called

I am receiving a body from my API call but onResponse() is not getting called, here are the methods:
final Rest_manager_league rest = new Rest_manager_league();
Call<List<Root>> listCall = rest.getMLeague_conn().getLeague(x);
listCall.enqueue(new Callback<List<Root>>() {
#Override
public void onResponse(Call<List<Root>> call, Response<List<Root>> response) {
lg = response.body();
Log.d("res", "ON");
if (response.isSuccessful()){
textView.setText(lg.get(3).getStanding().get(2).getTeamName());
Log.d("s", "true");
}
}
#Override
public void onFailure(Call<List<Root>> call, Throwable t) {
Log.d("Failure", "Failed");
}
});
Here is the Retrofit interface & the service:
public interface league_Conn {
#GET("/v1/soccerseasons/{id}/leagueTable")
#Headers("X-Auth-Token:" +
"1869f69f772b40a2a12fd6eefb4e48ef ")
Call<List<Root>> getLeague(#Path("id") int id);
}
public class Rest_manager_league {
private league_Conn mleague_conn;
public league_Conn getMLeague_conn() {
if (mleague_conn == null) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.football-data.org/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
mleague_conn = retrofit.create(league_Conn.class);
}
return mleague_conn;
}
}
In the logcat, onFailure() is showing up. Like so:
okhttp3 <-- END HTTP (8300 byte body) Failer :Failed
Why is onResponse() not getting called?
You are getting a response body (8300 bytes) but onFailure is getting called, because your returned body does not agree with your GSONFactory. The deserialization process did not work. You can pinpoint the problem by printing a stack trace as #yazan pointed out. Just type:
t.printStackTrace()
in onFailure().
Edit:
The error occurs because you're telling Retrofit that you're expecting a list but instead you're getting a JSON object. I took a quick look at the API that you're using and it looks like it returns a JSON object and the returned object then contains the list you're interested in accessing. Try replacing instances of List<Root> to just Root. For more help, you can also check this question:
GSON throws Expected BEGIN_ARRAY but was BEGIN_OBJECT error

Categories

Resources