Unable to parse error in Retrofit 2

怎甘沉沦 提交于 2019-12-05 06:44:08
BNK

UPDATE:

If you want to use a custom class instead of JSONObject below, you can refer to the following:

Custom class:

public class ResponseError {

    Error error;

    class Error {
        int statusCode;
        String message;
    }
}

Add the following to WebAPIService interface:

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

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

I have just tested your RetrofitClient class with my web service. I made a small update to your APIError class as the following (add 2 constructors, actually they are not called):

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

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

Interface:

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

My web service (Asp.Net Web API):

According to your JSON response data, I have used the following 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);
}

It's working! Hope it helps!

The problem was really strange and I cannot explain how was this happening, and my solution is really ugly.

Retrofit actually had two converter factories and the converter factory that it returned when I asked it to convert the response, was null.

I found out about this while I was debugging retrofit's method that returns converter factories. The second factory that it returned actually did the conversion successfully and I was finally able to parse my response. I still don't know what I did wrong.

Here is what ended up being my ugly solution:

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

The first time around in the loop I got a NPE. The second time I got the error message

Here is the error class I ended up using:

private class LoginError {
    Error error;

    class Error {
        String message;
        int statusCode;
    }
}

EDIT: I suppose it might be the WildCard's fault for confusing retrofit what converter to return, the one that I was passing here:

public static APIError parseError(Response<?> response, Retrofit retrofit) {
@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 my case GlobalErrorObject is a pojo representing JSON as :

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

** The above code is working for me.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!