2016-03-16 54 views
3

我正在處理API響應的Retrofit 2.0(這很棒)。 一切運作良好時API的答案與成功,我返回轉換對象從JSON響應Retrofit 2.0在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爲空

所以在這裏,我會打電話的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; 
    } 
} 
時,將其轉換成RestError POJO(下圖),並把它裏面調用

任何想法解決這個問題?

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(),似乎做的工作

你怎麼看?

+0

其實我有同樣的問題,我能得到響應如果它是200.But我想要的是從202獲得的東西。因此,對於200響應,我們已經創建了一個SuccessResponse GSON類。而對於202類,我還創建了另一個類,取決於JSON響應。 我無法同時使用兩者。我嘗試了Extending also.But沒有他們works.Any建議嗎? –

回答

0

您可以從一些Response類擴展所有POJO,其中將包含字段RestError error。然後你可以做一些isSuccessful的方法,這將檢查是否error字段null或沒有和正確處理它。

+0

我認爲這對於Retrofit 2.0是不正確的。如果HTTP狀態碼不是200,那麼responseBody爲空,並且您的POJO將爲空,因此在這種情況下擴展是無用的。 – LightMan

+0

@Geralt_Encore,我試過了,它並沒有解決我的問題 –

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); 
} 
+0

是的!在此期間,我以這種方式處理: TypeAdapter restErrorTypeAdapter = gson.getAdapter(RestError.class); RestError error = restErrorTypeAdapter.fromJsonTree(jsonElement); 但我仍然沒有任何想法如何發送的,而不是我的用戶POJO – ejay

+0

我的方法是這樣的錯誤: '類YourResponse { @JsonProperty(「數據」) 公衆的ArrayList 數據; @JsonProperty(「error」) public ArrayList 錯誤; (@Path(「userId」)String userId);' '如果你的數據!= null,那麼它不是一個錯誤響應。 我使用了Jackon,而不是Gson,但是這個想法是這樣的 – thuongle

+0

我更新了我的問題,你怎麼看待它? (感謝您的幫助) – ejay