39

我正在使用 Retrofit 库对我正在使用的服务进行 REST 调用。

如果我对我的服务进行 API 调用并出现故障,该服务会返回一些 JSON 以及标准 HTTP 错误内容。使用RetrofitError失败回调中包含的对象,我可以找到 HTTP 状态代码和其他一些内容,但是我无法检索服务发回的 JSON。

例如,假设我在尝试创建用户的地方调用 API。如果用户名已经存在,服务将返回一个 400 错误代码以及一些 JSON,如下所示:

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

因为一个简单的 400 错误代码不够具体,所以我真的需要访问返回的 JSON。

有谁知道我怎样才能得到这个 JSON 数据?我试过查看RetrofitError对象中的每个字段,但在任何地方都找不到。我还需要做些什么吗?

4

9 回答 9

104

您可以使用对象的getBodyAs方法RetrofitError。与其他 Retrofit 转换类似,它将响应转换为 Java 对象。首先定义一个描述 JSON 错误响应的类:

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

然后使用前面提到的方法来获取更详细描述错误的对象。

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改变了错误响应是转换器的方式。您需要使用responseBodyConverter方法获取正确的转换器,并使用它来转换响应的错误正文。除非异常处理,否则这将是:

Converter<ResponseBody, RestError> converter 
    = retrofit.responseBodyConverter(RestError.class, new Annotation[0]);
RestError errorResponse = converter.convert(response.errorBody());
于 2014-01-13T23:28:51.450 回答
20

试试这个代码

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

使用改造 2.0

@Override
public void onFailure(Call<Example> call, Throwable t) {
    String message = t.getMessage();
    Log.d("failure", message);
}
于 2014-09-20T14:11:05.777 回答
5

@LukaCiko 回答现在在改造 1.6.1 中对我不起作用。就像我现在正在做的那样:

    @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);
    }
于 2014-09-03T09:43:28.557 回答
5

getBodyAs用于获取 JSON 对象的两行代码。

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

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

确认在 Retrofit 1.x 中工作。不确定 Retrofit 2.x 需要进行哪些更改。

于 2015-10-21T21:08:25.270 回答
2

使用它,您可以获得错误正文

  if (response != null && response.errorBody() != null) {
    JSONObject jsonObject = new JSONObject(response.errorBody().string());
    String error =  jsonObject.getString("error");
  }
于 2016-07-13T17:05:07.407 回答
1

一种更简洁的方法是创建一个可以为您进行解析/转换的类,您可以随时随地调用它。

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

您现在可以在任何地方使用它,就像这样

ApiError error = new ApiError(error)
error.error

这全部来自这篇博文(我写的)。

于 2017-08-13T14:36:53.120 回答
0

我编译了许多答案并编写了一些代码来实现更好的效果:

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

我将“电子邮件”中的所有项目都显示为包含 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());
}
于 2014-09-12T17:09:44.350 回答
0

所以经过多打猎后,我找到了答案。

这是我的失败回调:

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

这是被调用来提取服务器错误的方法:

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

这将提取服务发送的以 JSON 编码的错误信息。

于 2013-12-16T23:20:01.323 回答
0

我也有同样的情况。我的问题是long来自服务器的字段为空String ""。改造是NumberFormatException因为 Gson 似乎没有将空字符串转换为long(我建议将其设为 0L 或其他内容)。所以我不得不改变:

long errorCode;

String errorCode;

如前所述,调试时我无法访问 JSON 消息。我终于使用 RequestMaker 页面找到了错误,也许它可以帮助其他人

http://requestmaker.com/

于 2015-07-15T10:25:27.947 回答