web-dev-qa-db-fra.com

Retrofit 2.0 comment obtenir une réponse d'erreur désérialisée

J'utilise Retrofit 2.0.0-beta1 .

Dans les tests, j'ai un scénario alternatif et j'attends l'erreur HTTP 400

Je voudrais avoir retrofit.Response<MyError> response Mais response.body() == null

MyError n'est pas désérialisé - je le vois seulement ici

response.errorBody().string()

mais cela ne me donne pas MyError comme objet

80
Piotr Boho

J'utilise actuellement une implémentation très simple, qui ne nécessite pas l'utilisation de convertisseurs ou de classes spéciales. Le code que j'utilise est le suivant:

public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
    DialogHelper.dismiss();

    if (response.isSuccessful()) {
        // Do your success stuff...
    } else {
        try {
            JSONObject jObjError = new JSONObject(response.errorBody().string());
            Toast.makeText(getContext(), jObjError.getString("message"), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}
87
Saif Bechan

Dans Retrofit 2.0 beta2, voici comment je reçois des réponses d'erreur:

  1. Synchrone 

    try {
       Call<RegistrationResponse> call = backendServiceApi.register(data.in.account, data.in.password,
               data.in.email);
       Response<RegistrationResponse> response = call.execute();
       if (response != null && !response.isSuccess() && response.errorBody() != null) {
           Converter<ResponseBody, BasicResponse> errorConverter =
                   MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
           BasicResponse error = errorConverter.convert(response.errorBody());
           //DO ERROR HANDLING HERE
           return;
       }
       RegistrationResponse registrationResponse = response.body();
       //DO SUCCESS HANDLING HERE
    } catch (IOException e) {
       //DO NETWORK ERROR HANDLING HERE
    }
    
  2. Asynchrone

    Call<BasicResponse> call = service.loadRepo();
    call.enqueue(new Callback<BasicResponse>() {
        @Override
        public void onResponse(Response<BasicResponse> response, Retrofit retrofit) {
            if (response != null && !response.isSuccess() && response.errorBody() != null) {
                Converter<ResponseBody, BasicResponse> errorConverter =
                    retrofit.responseConverter(BasicResponse.class, new Annotation[0]);
                BasicResponse error = errorConverter.convert(response.errorBody());
                //DO ERROR HANDLING HERE
                return;
            }
            RegistrationResponse registrationResponse = response.body();
            //DO SUCCESS HANDLING HERE
        }
    
        @Override
        public void onFailure(Throwable t) {
            //DO NETWORK ERROR HANDLING HERE
        }
    });
    

Mise à jour pour Retrofit 2 beta3:

  1. Synchrone - pas changé
  2. Asynchrone - Le paramètre Retrofit a été supprimé de onResponse

    Call<BasicResponse> call = service.loadRepo();
    call.enqueue(new Callback<BasicResponse>() {
        @Override
        public void onResponse(Response<BasicResponse> response) {
            if (response != null && !response.isSuccess() && response.errorBody() != null) {
                Converter<ResponseBody, BasicResponse> errorConverter =
                    MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
                BasicResponse error = errorConverter.convert(response.errorBody());
                //DO ERROR HANDLING HERE
                return;
            }
            RegistrationResponse registrationResponse = response.body();
            //DO SUCCESS HANDLING HERE
        }
    
        @Override
        public void onFailure(Throwable t) {
            //DO NETWORK ERROR HANDLING HERE
        }
    });
    
28
JFreeman

Je l'ai résolu par:

 if(!response.isSuccessful()){
       Gson gson = new Gson();
       MyErrorMessage message=gson.fromJson(response.errorBody().charStream(),MyErrorMessage.class);
       if(message.getCode()==ErrorCode.DUPLICATE_EMAIL_ID_CODE){
                  //DO Error Code specific handling                        
        }else{
                 //DO GENERAL Error Code Specific handling                               
        }
    }

Classe MyErrorMessage:

  public class MyErrorMessage {
     private int code;
     private String message;

     public int getCode() {
        return code;
     }

     public void setCode(int code) {
        this.code = code;
     }

     public String getMessage() {
         return message;
     }

     public void setMessage(String message) {
        this.message = message;
     }
   }
21
Pooja Gupta

Dans https://stackoverflow.com/a/21103420/2914140 et https://futurestud.io/tutorials/retrofit-2-simple-error-handling cette variante est présentée pour la modification ultérieure. 0.

call.enqueue(new Callback<MyResponse>() {
    @Override
    public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
        if (response.isSuccessful()) {
            ...
        } else {
            Converter<ResponseBody, MyError> converter
                    = MyApplication.getRetrofit().responseBodyConverter(
                    MyError.class, new Annotation[0]);
            MyError errorResponse = null;
            try {
                errorResponse = converter.convert(response.errorBody());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
8
CoolMind

Je l’ai fait de cette façon pour les appels asynchrones avec Retrofit 2.0-beta2:

@Override
public void onResponse(Response<RegistrationResponse> response, 
                       Retrofit retrofit) {
    if (response.isSuccess()) {
        // Do success handling here
    } else {
        try {
            MyError myError = (MyError)retrofit.responseConverter(
                    MyError.class, MyError.class.getAnnotations())
                .convert(response.errorBody());
            // Do error handling here
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
7
shantanu
 @Override
 public void onResponse(Call<Void> call, retrofit2.Response<Void> response) {
            if (response.isSuccessful()) {

            //Do something if response is ok
            } else {

                JsonParser parser = new JsonParser();
                JsonElement mJson = null;
                try {
                    mJson = parser.parse(response.errorBody().string());
                    Gson gson = new Gson();
                    MyError errorResponse = gson.fromJson(mJson, MyError.class);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }

            }
6
Vins

ErrorResponse est votre objet de réponse personnalisé

Kotlin 

val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
var errorResponse: ErrorResponse? = gson.fromJson(response.errorBody()!!.charStream(), type)

Java

Gson gson = new Gson();
Type type = new TypeToken<ErrorResponse>() {}.getType();
ErrorResponse errorResponse = gson.fromJson(response.errorBody.charStream(),type);
6
shadeup

Ainsi, vous n'avez pas besoin d'instance Retrofit si vous n'injectez qu'un service créé à partir de Retrofit. 

public class ErrorUtils {

  public static APIError parseError(Context context, Response<?> response) {

    APIError error = new APIError();

    try {
        Gson gson = new Gson();
        error = gson.fromJson(response.errorBody().charStream(), APIError.class);
    } catch (Exception e) {
        Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
    }

    if (TextUtils.isEmpty(error.getErrorMessage())) {
        error.setError(response.raw().message());
    }
    return error;
  }
}

Utilisez-le comme ceci:

if (response.isSuccessful()) {

      ...

    } else {

      String msg = ErrorUtils.parseError(fragment.getActivity(), response).getError(); // would be from your error class
      Snackbar.make(someview, msg, Snackbar.LENGTH_LONG).show();
    }
  }
3
Codeversed

Cela semble être le problème lorsque vous utilisez OkHttp avec Retrofit. Vous pouvez donc supprimer OkHttp ou utiliser le code ci-dessous pour obtenir le corps de l'erreur:

if (!response.isSuccessful()) {
 InputStream i = response.errorBody().byteStream();
 BufferedReader r = new BufferedReader(new InputStreamReader(i));
 StringBuilder errorResult = new StringBuilder();
 String line;
 try {
   while ((line = r.readLine()) != null) {
   errorResult.append(line).append('\n');
   }
 } catch (IOException e) { 
    e.printStackTrace(); 
}
}
2
KRUPEN GHETIYA

Je faisais face au même problème. Je l'ai résolu avec la modification. Laisse moi montrer ça ... 

Si votre structure d'erreur JSON ressemble à 

{
"error": {
    "status": "The email field is required."
}
}


My ErrorRespnce.Java 

public class ErrorResponce {

   @SerializedName("error")
   @Expose
   private ErrorStatus error;

   public ErrorStatus getError() {
      return error;
   }

   public void setError(ErrorStatus error) {
      this.error = error;
   }
}

Et ceci ma classe de statut d'erreur

public class ErrorStatus {

  @SerializedName("status")
  @Expose
  private String status;

  public String getStatus() {
      return status;
  }

  public void setStatus(String status) {
      this.status = status;
  }
}

Nous avons maintenant besoin d’une classe capable de gérer notre JSON. 

  public class ErrorUtils {

   public static ErrorResponce parseError (Response<?> response){
      Converter<ResponseBody , ErrorResponce> converter =          ApiClient.getClient().responseBodyConverter(ErrorResponce.class , new Annotation[0]);
    ErrorResponce errorResponce;
    try{
        errorResponce = converter.convert(response.errorBody());
    }catch (IOException e){
        return new ErrorResponce();
    }
    return errorResponce;
}
}

Maintenant, nous pouvons vérifier notre réponse lors de l'appel de l'API de modernisation. 

private void registrationRequest(String name , String email , String password , String c_password){


    final Call<RegistrationResponce> registrationResponceCall = apiInterface.getRegistration(name , email , password , c_password);
    registrationResponceCall.enqueue(new Callback<RegistrationResponce>() {
        @Override
        public void onResponse(Call<RegistrationResponce> call, Response<RegistrationResponce> response) {



            if (response.code() == 200){


            }else if (response.code() == 401){


                ErrorResponce errorResponce = ErrorUtils.parseError(response);
                Toast.makeText(MainActivity.this, ""+errorResponce.getError().getStatus(), Toast.LENGTH_SHORT).show();
            }
        }

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

        }
    });
}

Voilà, tu peux montrer ton pain grillé

2
pavel

Si vous utilisez Kotlin, une autre solution pourrait être simplement de créer une fonction d'extension pour la classe Response:

inline fun <reified T>Response<*>.parseErrJsonResponse(): T?
{
    val moshi = MyCustomMoshiBuilder().build()
    val parser = moshi.adapter(T::class.Java)
    val response = errorBody()?.string()
    if(response != null)
        try {
            return parser.fromJson(response)
        } catch(e: JsonDataException) {
            e.printStackTrace()
        }
    return null
}

Usage

val myError = response.parseErrJsonResponse<MyErrorResponse>()
if(myError != null) {
   // handle your error logic here
   // ...
}
1
Arsenius

Que diriez-vous d’une solution généralisée, je pense que quelque chose comme ceci pourrait fonctionner:

abstract class TestCallback<RESPONSE, ERROR extends Throwable> implements Callback<RESPONSE> {

    Class<ERROR> errorClass;
    Retrofit retrofit;

    TestCallback(Retrofit retrofit, Class<ERROR> errorClass) {
        this.retrofit = retrofit;
        this.errorClass = errorClass;
    }

    abstract void onSuccess(Call<RESPONSE> call, RESPONSE response);

    @Override
    public void onResponse(Call<RESPONSE> call, Response<RESPONSE> response) {
        if (response.isSuccessful()) {
            onSuccess(call, response.body());
            return;
        }

        if (response.errorBody() != null) {
            Converter<ResponseBody, ERROR> converter = retrofit.responseBodyConverter(errorClass, new Annotation[0]);
            ERROR error;
            try {
                error = converter.convert(response.errorBody());
                onFailure(call, error);
            } catch (IOException e) {
                // Conversion error. Add some meaningful message or return a custom error.                
                onFailure(call, new Throwable());
            }
        } else {
            // Unknown HTTP error (errorBody == null). Add some meaningful message or return a custom error.                
            onFailure(call, new Throwable());
        }
    }
}

Et ensuite, nous pouvons utiliser ce Callback<> personnalisé de la manière suivante:

Call<User> call = retrofit.create(UsersApi.class).signUp(email, password);
call.enqueue(new TestCallback<User, TestError>(retrofit, TestError.class) {
        @Override
        public void onFailure(Call<User> call, Throwable t) {
            if (t instanceof TestError) {

            } else {

            }
        }

        @Override
        void onSuccess(Call<User> call, User response) {
            // No need to check for isSuccessful() + no need to 
            // duplicate the same code for all of your handlers.
        }
    });
1
fraggjkee

Lisez errorBody sur une chaîne et analysez manuellement json.

 if(!response.isSuccessful()) {
    response.errorBody();

    String error = "";
    try {
        BufferedReader ereader = new BufferedReader(new InputStreamReader(
                response.errorBody().byteStream()));
        String eline = null;
        while ((eline = ereader.readLine()) != null) {
            error += eline + "";
        }
        ereader.close();
    } catch (Exception e) {
        error += e.getMessage();
    }
    Log.e("Error",error);

    try {
        JSONObject reader = new JSONObject(error);
        String message = reader.getString("message");

        Toast.makeText(context,message,Toast.LENGTH_SHORT).show();

    } catch (JSONException e) {
        e.printStackTrace();
    }
 }
1
Riyas PK
try{
                ResponseBody response = ((HttpException) t).response().errorBody();
                JSONObject json = new JSONObject( new String(response.bytes()) );
                errMsg = json.getString("message");
            }catch(JSONException e){
                return t.getMessage();
            }
            catch(IOException e){
                return t.getMessage();
            }
0
Mike6679

les valeurs errorBody doivent définir l'objet APIError dans Retrofit. Pour que vous puissiez utiliser la structure de code ci-dessous.

classe publique APIErrorUtils {

public static APIError parseError(Response<?> response) {
    Converter<ResponseBody, APIError> converter = API.getClient().responseBodyConverter(APIError.class, new Annotation[0]);

    APIError error;

    try {
        error = converter.convert(response.errorBody());
        Log.d("SERVICELOG", "****************************************************");
        Log.d("SERVICELOG", "***** SERVICE LOG");
        Log.d("SERVICELOG", "***** TIMESTAMP: " + String.valueOf(error.getTimestamp()));
        Log.d("SERVICELOG", "***** STATUS: " + String.valueOf(error.getStatus()));
        Log.d("SERVICELOG", "***** ERROR: " + error.getError());
        Log.d("SERVICELOG", "***** MESSAGE: " + error.getMessage());
        Log.d("SERVICELOG", "***** PATH: " + error.getPath());
        Log.d("SERVICELOG", "****************************************************");
    } catch (IOException e) {
        return new APIError();
    }

    return error;
}

}

APIError error = APIErrorUtils.parseError (response); If (error.getStatus () == 400) { ....}

0
Egemen Mede

résolu par:

Converter<MyError> converter = 
    (Converter<MyError>)JacksonConverterFactory.create().get(MyError.class);
MyError myError =  converter.fromBody(response.errorBody());
0
Piotr Boho

À Kotlin:

val call = APIClient.getInstance().signIn(AuthRequestWrapper(AuthRequest("1234567890z", "12341234", "nonce")))
call.enqueue(object : Callback<AuthResponse> {
    override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
        if (response.isSuccessful) {

        } else {
            val a = object : Annotation{}
            val errorConverter = RentalGeekClient.getRetrofitInstance().responseBodyConverter<AuthFailureResponse>(AuthFailureResponse::class.Java, arrayOf(a))
            val authFailureResponse = errorConverter.convert(response.errorBody())
        }
    }

    override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
    }
})
0
Adam Johns