Retrofit 2.0 OnFailure - Raw Response - android

I'm using retrofit to call a web service and retrofit is throwing a failure, the the message from the 'Throwable` is giving me
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
I'm assuming that this is because the .Net web service is throwing an error and not returning JSON. But to prove this I need to be able to see the raw response in the onFailure. Is there anyway I can do this?
this is the code I'm using
public void userLoginRequestEvent(final AuthenticateUserEvent event) {
Call call = sApi.login(event.getUsername(), event.getPassword(), OS_TYPE, DeviceInfoUtils.getDeviceName());
call.enqueue(new Callback<LoggedInUser>() {
#Override
public void onResponse(Response<LoggedInUser> response, Retrofit retrofit) {
// response.isSuccess() is true if the response code is 2xx
if (response.isSuccess()) {
LoggedInUser user = response.body();
AppBus.getInstance()
.post(new UserIsAuthenticatedEvent(user, event.getUsername(),
event.getPassword()));
} else {
int statusCode = response.code();
// handle request errors yourself
}
}
#Override
public void onFailure(Throwable t) {
// handle execution failures like no internet connectivity
Log.d("ERROR", t.getMessage());
}
});

You can use the log interceptor that exists in the okhttp-logging-interceptor.
A good example can be found in Logging with Retrofit 2 as well.

Your server answer is just a string, not an object. Use an Interceptor to see your received response.
Add incerceptor dependency
compile 'com.squareup.okhttp3:logging-interceptor:3.4.0'
and then add it to your custom OkHttp client.
OKHttp client = ....
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.interceptors().add(interceptor);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("url")
.client(client) // add custom OkHttp client
You can check for BASIC, HEADERS and BODY. In your case you check for BODY to see body that you send and what server is sending as response body.

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

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

Retrofit 2.0 : onResponse called with null when HTTP Code is 404

When REST Api returns 404, onResponse called with NULL Response. We have user search api and if the user is not found on the server our REST api returns 404 with error response in body with more details.
As Retrofit 2.0 returns a null response body, it's hard for us to show the correct error to the user.
Do we have an alternate solution to get a response during 404?
Response:
{"responseStatus":{"code":"00297","severity":"ERROR","message":"Profile not found..","info":"","status":404}}
HTTP Status Code:
404
Thanks
Retrofit 2.0 shouldn't be returning a null response. If you are making an async callback and want the handle the error it should look something like this
// Create a very simple REST adapter which points the GitHub API.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);
// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");
call.enqueue(new Callback<ArrayList<Item>>() {
#Override
public void onResponse(Call<ArrayList<Item>> call, Response<ArrayList<Item>> response) {
if(response.isSuccessful()) {
// do something
}
else {
Log.e("Error Code", String.valueOf(response.code()));
Log.e("Error Body", response.errorBody().toString());
//display the appropriate message...
}
}
#Override
public void onFailure(Call<ArrayList<Item>> call, Throwable t) {
}
});
Some of that code was copied from Retrofit Samples. If you are getting a null response on a 404 something else is going wrong.

Categories

Resources