square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
43.1k stars 7.3k forks source link

Converting response.errorBody() #1321

Closed jemshit closed 8 years ago

jemshit commented 8 years ago

Can somebody provide example of converting response.errorBody() to some Error class or String or JSON ? I've tried this kind of converter and this approach, but i always get null ?

In my case server responds with

"Message": "Authorization has been denied for this request."
jemshit commented 8 years ago

I have found this solution, i am not sure if it good solution. It converts errorBody to String:

BufferedReader reader = null;
StringBuilder sb = new StringBuilder();
try {
    reader = new BufferedReader(new InputStreamReader(response.errorBody().byteStream()));
    String line;
    try {
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
 } catch (IOException e) {
       e.printStackTrace();
 }

String finallyError = sb.toString();
jemshit commented 8 years ago

I've managed to do this with also Converter now. I think this is better solution. This is error returned from server:

"Message": "Authorization has been denied for this request."

I have this class for modeling this error:

public class ModelError {
    private String Message;

    public String getMessage() {
        return Message;
    }
    public void setMessage(String Message) {
        this.Message = Message;
    }
}

And this is code for getting error message from response.errorBody():

@Override
public void onResponse(Response<MyCustomModel> response, Retrofit retrofit) {
    if (response.errorBody() != null ) {                            
        Converter<ResponseBody, ModelError> errorConverter =
        retrofit.responseConverter(ModelError.class, new Annotation[0]);
        try {
            ModelError error = errorConverter.convert(response.errorBody());
        } catch (IOException e) {
            e.printStackTrace();
        }
        // Now use error.getMessage()
    }
    ...
}
philipgiuliani commented 8 years ago

Is there a new / better solution for this? The current retrofit2 snapshot does not provide Retrofit retrofit in the onResponse Callback anymore :(

responseConverter is now responseBodyConverter

Update: Working for retrofit2

if (response.isSuccess()) {
    // everything ok
} else {
    Converter<ResponseBody, ErrorResponse> converter = mApp.getRetrofit().responseBodyConverter(ErrorResponse.class, new Annotation[0]);

    try {
        ErrorResponse errors = converter.convert(response.errorBody());
        Toast.makeText(this, errors.getMessage(), Toast.LENGTH_SHORT).show();
    } catch (Exception e) {
        Toast.makeText(this, R.string.error_server_error, Toast.LENGTH_LONG).show();
    }
}
JakeWharton commented 8 years ago

You need to pass around the Retrofit or the Converter instance yourself.

On Tue, Dec 15, 2015, 5:41 AM Philip Giuliani notifications@github.com wrote:

Is there a new solution for this? The current retrofit2 snapshot does not provide Retrofit retrofit in the onResponse Callback anymore :(

— Reply to this email directly or view it on GitHub https://github.com/square/retrofit/issues/1321#issuecomment-164722592.

azizbekian commented 8 years ago

@JakeWharton , is it possible to covert to List? I can't manage.

JakeWharton commented 8 years ago
Type listOfString = new TypeToken<List<String>>() {}.getType();
mkjangid commented 8 years ago

I found a better way to convert errorbody to JSONObject if you dont have time for creating ResponseError classes. Just do JSONObject jObjError = new JSONObject (response.errorBody().string())

No silly converter required, no bufferedreader, plain old JSONObject as ususal

MrHamidKhan commented 8 years ago

How about if errorBody contains only a string?

JakeWharton commented 8 years ago

Just call .string() on it then

rajeefmk commented 8 years ago

@meetmkjangid Strangely, if you call response.errorBody().string() twice, the second time it returns empty.

Eg:

LoggerUtils.d(AppConstants.DEBUG_TAG, "Response first- " + response.errorBody().string());
LoggerUtils.d(AppConstants.DEBUG_TAG, "Response second - " + response.errorBody().string());

**Response**

D/Log: Response before- {"status":"error","code":"ERR037","message":"Incorrect Code"}
D/Log: Response after - 
JakeWharton commented 8 years ago

That's not strange, that's how a ResponseBody works. It's a resource you read.

On Mon, Oct 3, 2016, 6:46 PM RmK notifications@github.com wrote:

@meetmkjangid https://github.com/meetmkjangid Strangely, if you call response.errorBody().string() twice, (one inside log and one for parsing the data), the second time it returns empty.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/square/retrofit/issues/1321#issuecomment-251159005, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEESSDyOHC-y_GeoX5H5ZEztb_y5N0ks5qwTF8gaJpZM4GpCxz .

rajeefmk commented 8 years ago

Thanks @JakeWharton . My understanding was that it could be read any number of times. I guess that's not possible.

trinadhkoya commented 7 years ago

@philipgiuliani should i create ErrorResponse class as a POJO or POJO with Gson Annotations . It works like a Charm @philipgiuliani .Love you dude

msangel commented 7 years ago

I am migrating from retrofit1 and missing RetrofitError is like a nightmare. How do I rewrite this code?

public static Optional <JsonObject> getErrorMessage(Throwable e){
    if(RetrofitError.class.isInstance(e)){
        RetrofitError err = RetrofitError.class.cast(e);
        Object responce = err.getBody();
        if(JsonObject.class.isInstance(responce)){
            JsonObject res = JsonObject.class.cast(responce);
            return Optional.of(res);
        } else {
            return Optional.absent();
        }
    } else {
        return Optional.absent();
    }
}

The error API of 1st version was great.....

maheshsuthar93 commented 6 years ago

Any one help me to how to handle this type error in retrofit?? [ { "error": { "field": "data.first_name", "message": "must be first_name format" }, "message": "Alphabet characters are allowed in name." }, { "error": { "field": "data.first_name", "message": "must be first_name format" }, "message": "Alphabet characters are allowed in name." } ]

MrHamidKhan commented 6 years ago

Convert response.errorBody().string() to an array of your custom model. Use Gson or any other (de)serialization library for the conversion.

Share the StackOverflow link, I will provide the sample code.

IP696 commented 6 years ago

What about Dagger2? I think this "mApp.getRetrofit()" realization is not good. And Use GSON in each method not good too. Now I use my custom RestResponse class.

@Data
public class RestResponse<T> {
    private RestError error;
    private T response;

    @Data
    public static class RestError {
        private int code;
        private String message;
    }
}

Adn get error:

response.getError() in onSuccess:

@Override
        public void onSuccess(RestResponse<PaymentResponse> response) {
            if (response.getError() != null) {
                RestResponse.RestError error = response.getError();
                if (error.getCode() == 999999) {
                ..............

And I do not understand what is the point retrofit2 Response? can there be a normal nice way to convert an error into an object?

zhchzs commented 6 years ago

Why Call\<Entity> does not work when code is non-20x?

xenogew commented 5 years ago

Claps for @mkjangid answer, .string() is make life easier.

Why don't you ask yourself, we use library to make dev life easier, but why it's not be implemented from the start or just need to create stupid converter to see the plain text we want in its whole context.

mochadwi commented 4 years ago

if the server doesn't returns anything (obstacle like: you have no access to backend/3rd-party error, etc), and the errorBody() null or just empty,

If you did this

JSONObject jObjError = new JSONObject (response.errorBody().string())

it will throws: JSONException: Value of type java.lang.String cannot be converted to JSONObject

see answer:

  1. TL;DR answer here: https://stackoverflow.com/a/10268019/3763032
  2. wrap your strings with, e.g: new JSONObject("{your string}")
ali0399 commented 2 years ago

what is mApp?

sebastinto commented 2 years ago

if the server doesn't returns anything (obstacle like: you have no access to backend/3rd-party error, etc), and the errorBody() null or just empty,

If you did this

JSONObject jObjError = new JSONObject (response.errorBody().string())

it will throws: JSONException: Value of type java.lang.String cannot be converted to JSONObject

You could just wrap this in a try/catch block. It's good practice anyways.

Juliet-Olieng commented 1 year ago

help solve an error in this code class LoginViewModel:ViewModel() { val logRepo= LoginRepository() val logLiveData=MutableLiveData() val errLiveData=MutableLiveData<String?>()

fun loginUser(loginRequest: LoginRequest) {
    viewModelScope.launch {
        val response = logRepo.login(loginRequest)
        if (response.isSuccessful) {

        } else {
            val errorBody = response.errorBody()?.string() ?: "Unknown error" // Get the error message from the error response or use a default message if null
            errLiveData.postValue(errorBody)
        }
    }
}

}