2015-12-25 14 views
5

Ich bin mit Retrofit 2 und ich bin eine Null-Zeiger-Ausnahme an dieser Linie bekommen:kann nicht analysieren Fehler in Retrofit 2

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

Der Fehler ist null. Weitere Details:

Dies ist das Format, das die API den Fehler zurückgibt:

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

Hier ist mein Login Rückruf Code:

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(""); 
     } 
    } 
} 

Das ist mein Retrofit 2 Klasse:

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; 
      } 
     } 
    } 


} 

Ich habe die Fehler utils Klasse aus diesem Tutorial, aber änderte es leicht wegen der Fehlerformatierung in t Erben Beispiel unterscheidet:

EDIT: Dies ist die Converter Klasse:

/** 
* 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; 
    } 
    } 
} 

Antwort

5

UPDATE:

Wenn Sie statt JSONObject unter eine benutzerdefinierte Klasse verwenden möchten, können Sie auf das verweisen folgende:

Individuelle Klasse:

public class ResponseError { 

    Error error; 

    class Error { 
     int statusCode; 
     String message; 
    } 
} 

Folgendes WebAPIService Schnittstelle hinzufügen:

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

Dann innen 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()); 
    } 
}); 

Ich habe Ihre RetrofitClient Klasse mit meinem Web-Service gerade getestet. Ich habe ein kleines Update auf Ihre APIError Klasse wie die folgenden (2 Konstrukteurs hinzufügen, tatsächlich werden sie nicht genannt):

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

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

Schnittstelle:

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()); 
    } 
}); 

Meine Web Service (Asp.Net Web API):

Entsprechend Ihrer JSON Antwortdaten habe ich u sed den folgenden Code:

[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); 
} 

Es funktioniert! Ich hoffe es hilft!

+0

Wie initiiere ich es? Erstellen Sie einen Konstruktor für 'APIError', der keine Argumente annimmt und' loginError = new LoginError(); 'im Konstruktor verwendet? –

+0

versuchte das, kein Glück - 'public APIError() { loginError = new ErrorResponse(); } ' –

+0

Pls sehe meine aktualisierte Antwort – BNK

1

Das Problem war wirklich seltsam und ich kann nicht erklären, wie dies geschah, und meine Lösung ist wirklich hässlich.

Nachrüstung hatte eigentlich zwei Konverterfabriken und die Konverterfabrik, die zurückgegeben wurde, als ich darum bat, die Antwort zu konvertieren, war null.

Ich erfuhr darüber, während ich Debugging-Methode debugging, die Konverterfabriken zurückgibt. Die zweite Fabrik, die zurückgegeben wurde, hat die Konvertierung erfolgreich durchgeführt und ich konnte meine Antwort endlich analysieren. Ich weiß immer noch nicht, was ich falsch gemacht habe.

Hier ist, was meine hässliche Lösung endete als:

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

Das erste Mal ich in der Schleife eine NPE bekam. Das zweite Mal habe ich die Fehlermeldung

Hier ist die Fehlerklasse landete ich mit:

private class LoginError { 
    Error error; 

    class Error { 
     String message; 
     int statusCode; 
    } 
} 

EDIT: Ich nehme an, es könnte die WildCard Schuld für verwirrend nachgerüstet werden, was Konverter, die man zurückzuversetzen ich vorging hier:

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(); 
      } 

    } 
} 

** In meinem Fall GlobalErrorObject ein pojo ist darstellt JSON als:

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

** Der obige Code funktioniert bei mir.

Verwandte Themen