web-dev-qa-db-fra.com

Comment pouvons-nous gérer différents types de réponse avec Retrofit 2?

J'ai un webservice qui renvoie soit une liste d'objets sérialisés MyPOJO:

[
  { //JSON MyPOJO },
  { //JSON MyPOJO }
]

soit un objet d'erreur:

{  
  'error': 'foo', 
  'message':'bar' 
}

À l'aide de retrofit2, comment puis-je récupérer l'erreur?

Call<List<MyPOJO>> request = ...
request.enqueue(new Callback<List<MyPOJO>>() {
  @Override
  public void onResponse(Response<List<MyPOJO>> response) {
      if (response.isSuccess()) {
          List<MyPOJO> myList = response.body();
          // do something with the list...
      } else {
          // server responded with an error, here is how we are supposed to retrieve it
          ErrorResponse error = ErrorResponse.fromResponseBody(apiService.getRetrofitInstance(), response.errorBody());
          processError(error);
          // but we never get there because GSON deserialization throws an error !
      }
  }

  @Override
  public void onFailure(Throwable t) {
    if(t instanceof IOException){
        // network error 
    }else if(t instanceof IllegalStateException){
        // on server sending an error object we get there
        // how can I retrieve the error object ?
    }else { 
        // default error handling 
    }       
  }
}

Voici l'exception GSON:

Java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

L'instance de modification est créée à l'aide de GsonConverterFactory

24
Rémy DAVID

J'ai eu un problème similaire et je l'ai résolu en utilisant un générique Object puis en testant le type de réponse que j'ai eu en utilisant instanceof

Call<Object> call = api.login(username, password);
call.enqueue(new Callback<Object>() 
{
    @Override
    public void onResponse(Response<Object> response, Retrofit retrofit)
    {
         if (response.body() instanceof MyPOJO )
         {
            MyPOJO myObj = (MyPOJO) response.body();
            //handle MyPOJO 
         }
         else  //must be error object
         {
            MyError myError = (MyError) response.body();
            //handle error object
         }
    }

    @Override
    public void onFailure(Throwable t) 
    {
     ///Handle failure
    }
});

Dans mon cas, j'avais renvoyé MyPOJO ou MyError et je pouvais être sûr que ce serait l'un d'entre eux.

Dans d'autres cas, le backend a renvoyé le même objet de réponse, que la demande soit réussie ou non.
Puis, à l'intérieur de cet objet de réponse, j'avais mes données réelles dans un champ "Objet". Ensuite, je peux utiliser l'instance de pour déterminer le type de données que j'avais. Dans ce cas, j'ai toujours eu le même objet retourné, quel que soit l'appel.

public class MyResponse {

    private int responseCode;
    private String command;
    private int errorno;
    private String errorMessage;
    private Object responseObject;   //This object varies depending on what command was called
    private Date responseTime;
}
18
jason.kaisersmith
Call<LoginResponse> call = apiService.getUserLogin(usernametext, passwordtext);
    call.enqueue(new Callback<LoginResponse>() {
        @Override
        public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
            Log.e("responsedata","dd"+response.toString());
            if (response.code()==200) {
                showMessage(response.body().getMessage());
                Intent intent = new Intent(LoginActivity.this, MainAct.class);
                startActivity(intent);
            }
            else
                try {
                    LoginError loginError= gson.fromJson(response.errorBody().string(),LoginError.class);
                    showMessage(loginError.getMessage());
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

        @Override
        public void onFailure(Call<LoginResponse> call, Throwable t) {

        }
    });
}
10
Deven Mer