我正在研究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()并且似乎完成了工作
你怎么看?
答案 0 :(得分:0)
您可以从某个Response
类扩展所有POJO,其中包含字段RestError error
。然后,您可以制作一些isSuccessful
方法,该方法将检查error
字段null
是否正确处理。
答案 1 :(得分:0)
在我看来,您可以自定义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);
}