问题
I want to be able to deserialize network response to the same Java object even if response is not succesfull. Currently when I get an error response like 403, the response body is null, and I'm expected to use the response.errorBody() method to read whatever has been sent back, which is ok. However I want to avoid having alot of code in the retrofit callbacks just for deserializing errorBody. Instead I want to have an interceptor that sets the content of errorBody into body.
The reason for this is that I have a response object that has fields for both error and non error responses and depending on the status of the response some fields are expected to be left null e.g
Error Response JSON
{
"locked":true,
"remaining_attempts": 2
}
Success Response JSON
{
"name":"kev"
"token":"abcdefghijklmnopq"
}
I have created a single Java object that captures both scenarios:
class LoginResponse{
@Expose
private String name;
@Expose
private String token;
@Expose
private Boolean locked;
@Expose
private Integer remaining_attempts;
}
Is there way to do this in an interceptor?
回答1:
Retrofit is doing the serialization part by delegating it to the Converter, you you can add a specific one to the builder by using builder.addConverterFactory(GsonConverterFactory.create())
and there is already many written Retrofit Converters, you can find most of them here.
so if you want to control this process of deserialization, you can write your custom 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 addConverterFactory()
again to tell Retrofit about the new converter.
I should mention that you can use multiple converters in Retrofit which is awesome, it simply check converters by order till find the proper one to use.
resources: writing custom Retrofit converter, using multiple converters
回答2:
You can do something like following in response directly...
LoginResponse errorResponse = gson.fromJson(response.errorBody().string(), LoginResponse.class);
...or using 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;
}
};
}
来源:https://stackoverflow.com/questions/44907027/retrofit2-deserialize-response-body-even-if-response-is-not-200