首页 文章

Retrofit 2.0处理json响应中的错误

提问于
浏览
3

我正在研究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

PROBLEM: 但是当API回答错误时(也使用 Headers 中的代码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;
    }
}

有什么想法解决这个问题?

UPDATE:

我在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()并且似乎完成了这项工作

你有什么想法?

2 回答

  • 0

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

  • 0

    在我看来,您可以自定义您的 ItemTypeAdapterFactory 以检查响应是否是 data 或者是 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");
                 } 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);
    }
    

相关问题