2015-12-25 7 views
5

Я использую Дооснащение 2, и я получаю исключения нулевого указателя на этой линии:Невозможно разобрать ошибку в Модернизированный 2

RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit); 

ошибка равна нулю. Подробнее:

Это формат, что API возвращает ошибку:

{ 
    "error": { 
    "message": "Incorrect credentials", 
    "statusCode": 401 
    } 
} 

Вот мой Логин код Обратный звонок:

new Callback<LoginResponse>() { 

    @Override 
    public void onResponse(Response<LoginResponse> response, Retrofit retrofit) { 
     if (listener != null) { 
      if (response.isSuccess() && response.body() != null) { 

       User user = RetrofitUserToUserMapper.fromRetrofitUser(response.body().getLoginUser()); 

      } else { 

       RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit); 
       listener.onUserLoginFailure(error.getErrorMessage()); // NPE - error is null 
      } 
     } 
    } 

    @Override 
    public void onFailure(Throwable t) { 

     if (listener != null) { 
      listener.onUserLoginFailure(""); 
     } 
    } 
} 

Это мой дооснащения 2 класс:

public class RetrofitClient { 

    public static final String API_ROOT = "http://example.com/api/v1/"; 
    private static final String HEADER_OS_VERSION = "X-OS-Type"; 
    private static final String HEADER_APP_VERSION = "X-App-Version"; 
    private static final String HEADER_OS_VERSION_VALUE_ANDROID = "android"; 


    private RetrofitClient() { 
    } 

    private static Retrofit INSTANCE; 

    public static Retrofit getInstance() { 
     if (INSTANCE == null) { 
      setupRestClient(); 
     } 
     return INSTANCE; 
    } 

    public static void setupRestClient() { 

     OkHttpClient httpClient = new OkHttpClient(); 
     addHeadersRequiredForAllRequests(httpClient, BuildConfig.VERSION_NAME); 

     INSTANCE = new Retrofit.Builder() 
       .baseUrl(API_ROOT) 
       .addConverterFactory(GsonConverterFactory.create()) 
       .client(httpClient) 
       .build(); 
    } 

    private static void addHeadersRequiredForAllRequests(OkHttpClient httpClient, final String appVersion) { 

     class RequestInterceptor implements Interceptor { 

      @Override 
      public com.squareup.okhttp.Response intercept(Chain chain) throws IOException { 
       Request request = chain.request().newBuilder() 
         .addHeader(HEADER_OS_VERSION, HEADER_OS_VERSION_VALUE_ANDROID) 
         .addHeader(HEADER_APP_VERSION, appVersion) 
         .build(); 
       return chain.proceed(request); 
      } 
     } 

     httpClient.networkInterceptors().add(new RequestInterceptor()); 
    } 

    public static class ErrorUtils { 

     public static APIError parseError(Response<?> response, Retrofit retrofit) { 
      Converter<ResponseBody, APIError> converter = 
        retrofit.responseConverter(APIError.class, new Annotation[0]); 

      APIError error; 

      try { 
       error = converter.convert(response.errorBody()); 
      } catch (IOException e) { 
       e.printStackTrace(); 
       return new APIError(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
       return new APIError(); 
      } 

      return error; 
     } 
    } 

    public static class APIError { 
     @SerializedName("error") 
     public 
     ErrorResponse loginError; 

     public ErrorResponse getLoginError() { 
      return loginError; 
     } 

     public String getErrorMessage() { 
      return loginError.message; 
     } 

     private class ErrorResponse { 
      @SerializedName("message") 
      private String message; 
      @SerializedName("statusCode") 
      public int statusCode; 

      public String getMessage() { 
       return message; 
      } 

      public int getStatusCode() { 
       return statusCode; 
      } 

      @Override 
      public String toString() { 
       return "LoginErrorResponseBody{" + 
         "message='" + getMessage() + '\'' + 
         ", statusCode=" + statusCode + 
         '}'; 
      } 

      public void setMessage(String message) { 
       this.message = message; 
      } 
     } 
    } 


} 

Я получил класс utils ошибки из этого учебника, но немного изменил его, поскольку форматирование ошибки в t Пример Наследник отличается:

EDIT: Это Converter класс:

/** 
* Convert objects to and from their representation as HTTP bodies. Register a converter with 
* Retrofit using {@link Retrofit.Builder#addConverterFactory(Factory)}. 
*/ 
public interface Converter<F, T> { 
    T convert(F value) throws IOException; 

    abstract class Factory { 
    /** 
    * Create a {@link Converter} for converting an HTTP response body to {@code type} or null if it 
    * cannot be handled by this factory. 
    */ 
    public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) { 
     return null; 
    } 

    /** 
    * Create a {@link Converter} for converting {@code type} to an HTTP request body or null if it 
    * cannot be handled by this factory. 
    */ 
    public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) { 
     return null; 
    } 
    } 
} 

ответ

5

UPDATE:

Если вы хотите использовать пользовательский класс вместо JSONObject ниже, вы можете обратиться к следующее:

Пользовательский класс:

public class ResponseError { 

    Error error; 

    class Error { 
     int statusCode; 
     String message; 
    } 
} 

Добавьте следующие WebAPIService интерфейса:

@GET("/api/geterror") 
Call<ResponseError> getError2(); 

Затем внутри MainActivity.java:

Call<ResponseError> responseErrorCall = service.getError2(); 
responseErrorCall.enqueue(new Callback<ResponseError>() { 
    @Override 
    public void onResponse(Response<ResponseError> response, Retrofit retrofit) { 
     if (response.isSuccess() && response.body() != null){ 
      Log.i(LOG_TAG, response.body().toString()); 
     } else { 
      if (response.errorBody() != null){ 
       RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit); 
       Log.e(LOG_TAG, error.getErrorMessage()); 
      } 
     } 
    } 

    @Override 
    public void onFailure(Throwable t) { 
     Log.e(LOG_TAG, t.toString()); 
    } 
}); 

Я просто проверял свой RetrofitClient класс с моей веб-службы. Я сделал небольшое обновление для вашего APIError класса, как показано ниже (добавить 2 конструкторами, на самом деле они не называются):

public APIError(){ 
    this.loginError = new ErrorResponse(); 
} 

public APIError(int statusCode, String message) { 
    this.loginError = new ErrorResponse(); 
    this.loginError.statusCode = statusCode; 
    this.loginError.message = message; 
} 

Интерфейс:

public interface WebAPIService { 
    @GET("/api/geterror") 
    Call<JSONObject> getError(); 
} 

MainActivity:

// Retrofit 2.0-beta2 
Retrofit retrofit = new Retrofit.Builder() 
     .baseUrl(API_URL_BASE) 
     .addConverterFactory(GsonConverterFactory.create()) 
     .build(); 

WebAPIService service = retrofit.create(WebAPIService.class); 

Call<JSONObject> jsonObjectCall = service.getError(); 
jsonObjectCall.enqueue(new Callback<JSONObject>() { 
    @Override 
    public void onResponse(Response<JSONObject> response, Retrofit retrofit) { 
     if (response.isSuccess() && response.body() != null){ 
      Log.i(LOG_TAG, response.body().toString()); 
     } else { 
      if (response.errorBody() != null){ 
       RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit); 
       Log.e(LOG_TAG, error.getErrorMessage()); 
      } 
     } 
    } 

    @Override 
    public void onFailure(Throwable t) { 
     Log.e(LOG_TAG, t.toString()); 
    } 
}); 

Мои веб-сервис (API-интерфейс Asp.Net):

Согласно вашим данным ответа JSON, у меня есть u sed следующий код:

[Route("api/geterror")] 
public HttpResponseMessage GetError() 
{ 
    var detailError = new 
    { 
     message = "Incorrect credentials", 
     statusCode = 401 
    }; 

    var myError = new 
    { 
     error = detailError 
    };    

    return Request.CreateResponse(HttpStatusCode.Unauthorized, myError); 
} 

Это работает! Надеюсь, поможет!

+0

Как его инициализировать? Создайте конструктор для 'APIError', который не принимает никаких аргументов и делает' loginError = new LoginError(); 'внутри конструктора? –

+0

попробовал это, не повезло - 'public APIError() { loginError = new ErrorResponse(); } ' –

+0

Pls см. Мой обновленный ответ – BNK

1

Проблема была действительно странной, и я не могу объяснить, как это произошло, и мое решение действительно уродливо.

У доработки на самом деле были две фабрики преобразователей и завод преобразователя, которые он вернул, когда я попросил его преобразовать ответ, был нулевым.

Я узнал об этом, пока я отлаживал метод дооснащения, который возвращает фабрики конвертеров. Второй завод, который он вернул, действительно успешно завершил конверсию, и я наконец смог разобрать мой ответ. Я до сих пор не знаю, что я сделал не так.

Вот что в конечном итоге быть моим некрасивым решением:

String errorMessage = ""; 
for (Converter.Factory factory : retrofit.converterFactories()) { 
    try { 
     LoginError loginError = (LoginError) factory.fromResponseBody(LoginError.class, new Annotation[0]).convert(errorBody); 
     if (loginError != null) { 
      errorMessage = loginError.error.message; 
     } 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 
if (!TextUtils.isEmpty(errorMessage)) { 
    listener.onUserLoginFailure(errorMessage); 
} 

Первый раз в цикле я получил NPE. Второй раз, когда я получил сообщение об ошибке

Вот класс ошибок я в конечном итоге с помощью:

private class LoginError { 
    Error error; 

    class Error { 
     String message; 
     int statusCode; 
    } 
} 

EDIT: Я предполагаю, что это может быть ошибкой Подстановочной для запутанного дооснащения, что конвертер для возврата, тот, который Я проходил здесь:

public static APIError parseError(Response<?> response, Retrofit retrofit) { 
0
@Override 
public void onResponse(Response<User> response, Retrofit retrofit) { 
    if (response.isSuccess()) { 
     User user = response.body; 
     Log.e("User name", user.getName()); // do whatever you want 
    }else{ 
     Converter<GlobalErrorObject> converter = 
        (Converter<GlobalErrorObject>) GsonConverterFactory.create().get(GlobalErrorObject.class); 
      try { 
       GlobalErrorObject globalErrorObject = converter.fromBody(response.errorBody()); 
       Log.e("Error", globalErrorObject.getErrorMessage()); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 

    } 
} 

** В моем случае GlobalErrorObject является POJO, представляющий JSON как:

{ 
    "errorCode": "API_INVALID_TOKEN", 
    "errorType": "API_ERROR", 
    "errorMessage": "Valid API Token required." 
} 

** Приведенный выше код работает для меня.

 Смежные вопросы

  • Нет связанных вопросов^_^