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?
source to share
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
source to share
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;
}
};
}
source to share