【问题标题】:Retrofit 2.0 handle error in json responseRetrofit 2.0 处理 json 响应中的错误
【发布时间】:2016-07-02 08:12:26
【问题描述】:

我正在开发 Retrofit 2.0(非常棒)来处理 API 响应。 当 API 成功回答时一切正常,我从 json 响应返回希望的转换对象

这里是请求示例:

ServiceAPI.getUser(new Callback<User>() {
    @Override
    public void onResponse(Call<User> call, Response<User> response) {
       User user = response.body(); // user is my POJO
    }

    @Override
    public void onFailure(Call<User> call, final Throwable t) {
        Log.e(TAG, "Error: " + t.getMessage())
    }
});

响应完成后,我返回 POJO(这是我的主要目的),而不是要解析的 json,以避免样板。

所以这里是我的代码来处理这个:

// the interface to handle calls
protected interface ServiceAPI {
    @GET("/user/{userId}")
    Call<User> getUser(@Path("userId") String userId);
}

// the GSON part for converting data
Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new ItemTypeAdapterFactory())
    .setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
    .create();

// an interceptor to log requests responses
OkHttpClient okClient = new OkHttpClient.Builder()
    .addInterceptor(new LogJsonInterceptor())
    .build();

// the retrofit builder
retrofit = new Retrofit.Builder()
    .baseUrl(mBaseUrl)
    .client(okClient)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build();

// the interceptor to log requests
public static class LogJsonInterceptor implements Interceptor {
    @Override
    public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();
        okhttp3.Response response = chain.proceed(request);
        String rawJson = response.body().string();
        Log.d(TAG, rawJson);
        // Re-create the response before returning it because body can be read only once
        return response.newBuilder().body(ResponseBody.create(response.body().contentType(), rawJson)).build();
    }
}

// here the magic to handle json response and get data from "data" json key
public static class ItemTypeAdapterFactory implements TypeAdapterFactory {

    public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {

        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {

            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            private RestError error;

            public T read(JsonReader in) throws IOException {

                JsonElement jsonElement = elementAdapter.read(in);
                if (jsonElement.isJsonObject()) {
                    JsonObject jsonObject = jsonElement.getAsJsonObject();
                    if (jsonObject.has("data")) {
                        jsonElement = jsonObject.get("data");
                    }
                }

                return delegate.fromJsonTree(jsonElement);
            }
        }.nullSafe();
    }
}

// here the method to call from an activity for example to get an User
public static void getUser(final String userId, final Callback<User> callback) {
    serviceAPI.getUser(userId).enqueue(new Callback<User>() {
        @Override
        public void onResponse(Call<User> call, Response<User> response) {
            callback.onResponse(call, response);
        }

        @Override
        public void onFailure(Call<User> call, Throwable t) {
            callback.onFailure(call, t);
        }
    });
}

// here the call from the Activity
ServiceAPI.getUser(new Callback<User>() {
    @Override
    public void onResponse(Call<User> call, Response<User> response) {
       User user = response.body();
    }

    @Override
    public void onFailure(Call<User> call, final Throwable t) {
        Log.e(TAG, "Error: " + t.getMessage())
    }
});

// when API answers with success (code 200 from headers)
{
"data":
    {
        "id":1,
        "name":"myname",
        "email":"myemail"
    }
}

所以这里一切正常,因为我从“数据”得到响应并将响应转换为我的 POJO

问题: 但是当 API 回答错误时(也有来自标头的代码 200),我得到了这个:

{
"error":
    {
        "code":200
        "type":"OAuth_exception",
        "message":"You need an access token to get an user",
    }
}

问题是改造“响应”仍然成功并且errorBody为空

所以在这里,我会将其转换为 RestError POJO(如下)并在调用 getUser 方法时将其发送到调用中

public class RestError {
    private int code;
    private String message;
    private String type;

    public RestError(int code, String message, String type) {
        this.code = code;
        this.message = message;
        this.type = type;
    }
}

有办法解决这个问题吗?

更新:

我在 ItemTypeAdatperFactory 中添加了这个

public T read(JsonReader in) throws IOException {

                    JsonElement jsonElement = elementAdapter.read(in);
                    if (jsonElement.isJsonObject()) {
                        JsonObject jsonObject = jsonElement.getAsJsonObject();
                        if (jsonObject.has("data")) {
                            jsonElement = jsonObject.get("data");
                        } else if (jsonObject.has("error")) {
                            jsonElement = jsonObject.get("error");

                            TypeAdapter<RestError> restErrorTypeAdapter = gson.getAdapter(RestError.class);
                            RestError error = restErrorTypeAdapter.fromJsonTree(jsonElement);
                            return (T) error;
                        }
                    }

                    return delegate.fromJsonTree(jsonElement);
                }

我创建了一个这样的自定义回调:

public abstract static class CustomCallback<T> implements Callback<T> {

    public abstract void onError(RestError error);
    public abstract void onSuccess(T body);

    @Override
    public void onResponse(Call<T> call, Response<T> response) {
        if(response.body() instanceof RestError) {
            onError((RestError) response.body());
        } else {
            onSuccess(response.body());
        }
    }

    @Override
    public void onFailure(Call<T> call, Throwable t) {
        Log.e(TAG, t.toString());
    }
}

那么现在调用是怎样的:

ServiceAPI.getUser(new ServiceAPI.CustomCallback<User>() {
        @Override
        public void onError(RestError error) {
            Log.e(TAG, error.toString());
        }

        @Override
        public void onSuccess(User body) {
            Log.d(TAG, body.toString());  
            User user = body;  
        }

        @Override
        public void onFailure(Call<User> call, Throwable t) {
            Log.e(TAG, t.toString());
        }
    });

所以现在当发生错误时,我从 onError() 中获取它,否则 onSuccess() 并且似乎可以完成这项工作

你怎么看?

【问题讨论】:

  • 我实际上遇到了同样的问题,如果它是 200,我能够获得响应。但我想要的是从 202 获取东西。所以对于 200 响应,我们已经创建了一个 SuccessResponse GSON 类。对于 202 类,我还创建了另一个依赖于 JSON 响应的类。我无法同时使用两者。我也尝试过扩展。但它们都不起作用。请问有什么建议吗?

标签: android retrofit2


【解决方案1】:

在我看来,您可以像这样自定义您的ItemTypeAdapterFactory 以检查响应是您的数据还是错误

public T read(JsonReader in) throws IOException {
     JsonElement jsonElement = elementAdapter.read(in);
         if (jsonElement.isJsonObject()) {
             JsonObject jsonObject = jsonElement.getAsJsonObject();
             if (jsonObject.has("data")) {
                 jsonElement = jsonObject.get("data");
             } else if(jsonObject.has("error"){
                 jsonElement = jsonObject.get("error");
             }
         }
    //TODO: need to handle your parsing here (to data or to error)
    return delegate.fromJsonTree(jsonElement);
} 

【讨论】:

  • 是的!与此同时,我以这种方式处理: TypeAdapter restErrorTypeAdapter = gson.getAdapter(RestError.class); RestError 错误 = restErrorTypeAdapter.fromJsonTree(jsonElement);但我仍然不知道如何发送此错误而不是我的用户 POJO
  • 我的方法是:class YourResponse{ @JsonProperty("data") public ArrayList&lt;User&gt; data; @JsonProperty("error") public ArrayList&lt;ErrorResponse&gt; error; }Call&lt;YourResponse&gt; getUser(@Path("userId") String userId);如果你的数据!= null,那么它不是错误响应。我使用的是 Jackon,而不是 Gson,但这个想法是类似的
  • 我更新了我的问题,你怎么看? (感谢您的帮助)
  • 那么你们觉得这个解决方案怎么样?
  • 您能否将您的代码发送到我的电子邮件地址 thuongle.it@gmail.com。我将在我的 IDE 中检查它。也许我可以给你一个解决方案
【解决方案2】:

您可以从某个 Response 类扩展所有 POJO,该类将包含字段 RestError error。然后你可以做一些isSuccessful方法,它会检查error字段null是否正确处理。

【讨论】:

  • 我认为这对于 Retrofit 2.0 是不正确的。如果HTTP状态码不是200,那么responseBody就是null,你的POJO就是null,这种情况下扩展没用。
  • @Geralt_Encore,我试过了,它没有解决我的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-14
  • 2016-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-22
  • 1970-01-01
相关资源
最近更新 更多