Retrofit2 Deserialize response body even if response is not 200

I want to deserialize a network response to the same Java object, even if the response is not successful. Currently, when I get an error response like 403, the response body is null and I have to use the response.errorBody () method to read whatever was sent back, which is fine. However, I want to avoid using a lot of code in retro callbacks just to deserialize the errorBody. Instead, I want to have an interceptor that sets the content of the errorBody to the body.

The reason for this is that I have a response object that has fields for error and error responses, and depending on the status of the response, some fields are expected to remain empty, for example

JSON response error

{
"locked":true,
"remaining_attempts": 2
}

      

JSON success response

{
"name":"kev"
"token":"abcdefghijklmnopq"
}

      

I created one Java object that displays both scripts:

class LoginResponse{
    @Expose
    private String name;
    @Expose
    private String token;
    @Expose
    private Boolean locked;
    @Expose
    private Integer remaining_attempts;
}

      

Is there a way to do this in an interceptor?

+3


source to share


2 answers


Retrofit does the serialization part by delegating it to the converter, you can add a specific constructor to the constructor using builder.addConverterFactory(GsonConverterFactory.create())

and there are already many written Converters, you can find most of them here .

so if you want to control this deserialization process you can write your own converter, something like this

public class UnwrapConverterFactory extends Converter.Factory {

    private GsonConverterFactory factory;

    public UnwrapConverterFactory(GsonConverterFactory factory) {
        this.factory = factory;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(final Type type,
            Annotation[] annotations, Retrofit retrofit) {
        // e.g. WrappedResponse<Person>
        Type wrappedType = new ParameterizedType() {
            @Override
            public Type[] getActualTypeArguments() {
                // -> WrappedResponse<type>
                return new Type[] {type};
            }

            @Override
            public Type getOwnerType() {
                return null;
            }

            @Override
            public Type getRawType() {
                return WrappedResponse.class;
            }
        };
        Converter<ResponseBody, ?> gsonConverter = factory
                .responseBodyConverter(wrappedType, annotations, retrofit);
        return new WrappedResponseBodyConverter(gsonConverter);
    }
}

      



then you use again addConverterFactory()

to tell about the new converter update. I must mention that you can use multiple converters in Retrofit, which is awesome, just check the converters in order until you find one that works.

Resources: record custom converter Retrofit , using multiple converters

+1


source


You can do something like the following in direct response ...

    LoginResponse errorResponse = gson.fromJson(response.errorBody().string(), LoginResponse.class);

      



... or using an interceptor

addInterceptor(getMyErrorResponseInterceptor())


protected Interceptor getMyErrorResponseInterceptor() {
    return new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
            if (response.code() >= 400) {

                // handle error
            }

            return response;
        }
    };
}

      

0


source







All Articles