web-dev-qa-db-fra.com

Obtention de JSON à partir d'un objet RetrofitError à l'aide de Retrofit

J'utilise la bibliothèque Retrofit pour faire REST appels vers un service que j'utilise.

Si j'effectue un appel API à mon service et que j'échoue, le service renvoie un peu de JSON avec les erreurs d'erreur HTTP standard. En utilisant l'objet RetrofitError inclus dans le rappel d'échec, je suis en mesure de trouver le code d'état HTTP et plusieurs autres choses, mais je ne suis pas en mesure de récupérer le JSON que le service renvoie.

Par exemple, supposons que j'appelle l'API où j'essaie de créer un utilisateur. Si le nom d'utilisateur existe déjà, le service renverra un code d'erreur 400 avec du JSON comme ceci:

{"error":"Username already in use"}

Parce qu'un simple code d'erreur 400 n'est pas assez spécifique, j'ai vraiment besoin d'accéder au JSON qui est retourné.

Est-ce que quelqu'un sait comment obtenir ces données JSON? J'ai essayé de regarder chaque champ de l'objet RetrofitError et je ne le trouve nulle part. Y a-t-il quelque chose que je dois faire de plus?

39
neonDion

Vous pouvez utiliser la méthode getBodyAs de l'objet RetrofitError. Il convertit la réponse en un objet Java de manière similaire aux autres conversions Retrofit. Définissez d'abord une classe qui décrit votre réponse d'erreur JSON:

class RestError {
    @SerializedName("code")
    public int code;
    @SerializedName("error")
    public String errorDetails;
}

Utilisez ensuite la méthode mentionnée précédemment pour obtenir l'objet qui décrit l'erreur plus en détail.

catch(RetrofitError error) {
    if (error.getResponse() != null) {
        RestError body = (RestError) error.getBodyAs(RestError.class);
        log(body.errorDetails);
        switch (body.code) {
            case 101:
                ...
            case 102:
                ...
        }
    }
}

Retrofit 2.0 a changé la façon dont les réponses aux erreurs sont converties. Vous devrez obtenir le bon convertisseur avec la méthode responseBodyConverter et l'utiliser pour convertir le corps de l'erreur de la réponse. Sauf gestion des exceptions, ce serait:

Converter<ResponseBody, RestError> converter 
    = retrofit.responseBodyConverter(RestError.class, new Annotation[0]);
RestError errorResponse = converter.convert(response.errorBody());
103
LukaCiko

Essayez ce code

@Override
public void failure(RetrofitError error) {
    String json =  new String(((TypedByteArray)error.getResponse().getBody()).getBytes());
    Log.v("failure", json.toString());
}

avec Retrofit 2.

@Override
public void onFailure(Call<Example> call, Throwable t) {
    String message = t.getMessage();
    Log.d("failure", message);
}
20
Cabezas

Deux lignes avec getBodyAs pour obtenir un objet JSON.

JsonObject responseAsJson = (JsonObject) retrofitError.getBodyAs(JsonElement.class);

String message = responseAsJson.get("error").getAsString(); //=> "Username already in use"

Travail confirmé dans Retrofit 1.x. Je ne sais pas quelles modifications sont nécessaires pour Retrofit 2.x.

5
JaredBanyard

La réponse @LukaCiko ne fonctionne pas maintenant pour moi dans la mise à niveau 1.6.1. Ici comme je le fais maintenant:

    @Override
    public void failure(RetrofitError retrofitError) {
        String json =  new String(((TypedByteArray)retrofitError.getResponse().getBody()).getBytes());
        //own logic for example
        ExampleHandlerError error = new Gson().fromJson(json, ExampleHandlerError.class);
    }
5
ar-g

En utilisant cela, vous pouvez obtenir le corps de l'erreur

  if (response != null && response.errorBody() != null) {
    JSONObject jsonObject = new JSONObject(response.errorBody().string());
    String error =  jsonObject.getString("error");
  }
1
Amit Shekhar

Une façon beaucoup plus simple de le faire est de créer une classe qui peut effectuer l'analyse/conversion pour vous, une classe que vous pouvez appeler à tout moment, n'importe où.

public class ApiError {
    public String error = "An error occurred";

    public ApiError(Throwable error) {
        if (error instanceof HttpException) {
            String errorJsonString = null;
            try {
                errorJsonString = ((HttpException) 
                error).response().errorBody().string();
            } catch (IOException e) {
                e.printStackTrace();
            }
            JsonElement parsedString = new 
            JsonParser().parse(errorJsonString);
            this.error = parsedString.getAsJsonObject()
                                     .get("error")
                                     .getAsString();
        } else {
            this.error = error.getMessage() != null ? error.getMessage() : this.error;
        }
    }
}

Vous pouvez maintenant l'utiliser partout, comme ça

ApiError error = new ApiError(error)
error.error

Tout cela vient de ce blog (que j'ai écrit).

1
Timi Ajiboye

Donc, après un peu de chasse, j'ai trouvé la réponse.

Voici mon rappel d'échec:

@Override public void failure(RetrofitError retrofitError) {
    String serverError = null;
    try {
        serverError = extractServerError(retrofitError.getResponse().getBody().in());
    } catch (Exception e) {
        Log.e("LOG_TAG", "Error converting RetrofitError server JSON", e);
    }

    if (serverError!=null) {
        Log.i(LOG_TAG, serverError);
    }

    Intent intent = new Intent(ACTION_REGISTRATION_ERROR);
    intent.putExtra("ServerError", serverError);
    LocalBroadcastManager.getInstance(FamDooApplication.CONTEXT).sendBroadcastSync(intent);
}

Et voici la méthode qui est appelée pour extraire l'erreur du serveur:

public static String extractServerError(Java.io.InputStream is) {
    String serverError = null;
    String serverErrorDescription = null;
try {

    String s = convertStreamToString(is);

    JSONObject messageObject = new JSONObject(s);
    serverError = messageObject.optString("error");
    serverErrorDescription = messageObject.optString("error_description");
    if (serverErrorDescription!=null && !serverErrorDescription.equals("")) {
        return serverErrorDescription;
    } else {
        return serverError;
    }
    //String serverStack = messageObject.getString("stack");
} catch (Exception e) {
    Log.e("Basemodel", "Error converting RetrofitError server JSON", e);
}

return "";
}

Cela extraira les informations d'erreur envoyées par le service qui sont encodées dans le JSON.

0
neonDion

J'ai compilé de nombreuses réponses et écrit du code pour réaliser quelque chose de plus agréable:

{
    "errors": {
        "email": [
            "Email not valid.",
            "Unable to find the user [email protected]."
        ]
    }
}

Je prends tous les éléments dans 'e-mail' et je les affiche avec Guava Joiner:

String json =  new String(((TypedByteArray)error.getResponse()
    .getBody()).getBytes());

Map<String, Object> map = new Gson().fromJson(
    json, new TypeToken<Map<String, Map<String, List<String>>>>() {}.getType());

try {
    List<String> errorsEmail = 
        (List<String>) ((Map)map.get("errors")).get("email");
    Toast.makeText(getApplicationContext(), Joiner.on("\n")
        .join(errorsEmail), Toast.LENGTH_SHORT).show();
} catch(Exception e){
    Log.e(Constants.TAG, e.getMessage());
}
0
Hugo Gresse

J'avais la même chose. Mon problème était un champ long qui provenait du serveur en tant que String"" Vide. Retrofit donnait NumberFormatException car il semble que Gson ne convertit pas une chaîne vide en long (je suggère de lui donner 0L ou quelque chose). J'ai donc dû changer:

long errorCode;

à

String errorCode;

Comme cela a été dit précédemment, je n'avais pas accès au message JSON lors du débogage. J'ai finalement trouvé l'erreur en utilisant la page RequestMaker, peut-être que cela aide quelqu'un d'autre

http://requestmaker.com/

0
voghDev