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?
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());
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);
}
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.
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);
}
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");
}
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).
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.
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());
}
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