Closed kevinvandenbreemen closed 2 years ago
You can mix in this behavior with a custom Converter.Factory
and a union type that holds both the raw and deserialized body.
Here's a full example:
final class WithRawBody<T> {
private final ResponseBody raw;
private final T body;
public WithRawBody(ResponseBody raw, T body) {
this.raw = raw;
this.body = body;
}
public ResponseBody raw() {
return raw;
}
public T body() {
return body;
}
}
final class WithRawBodyConverterFactory extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (getRawType(type) != WithRawBody.class) {
return null;
}
if (!(type instanceof ParameterizedType)) {
throw new IllegalArgumentException("WithRawBody must have generic type");
}
Type bodyType = getParameterUpperBound(0, ((ParameterizedType) type));
Converter<ResponseBody, Object> bodyConverter =
retrofit.responseBodyConverter(bodyType, annotations);
return (Converter<ResponseBody, Object>) value -> {
byte[] rawBody = value.source().peek().readByteArray();
ResponseBody rawResponse = ResponseBody.create(value.contentType(), rawBody);
Object body = bodyConverter.convert(value);
return new WithRawBody<>(rawResponse, body);
};
}
}
interface ExampleService {
@GET("user")
Call<WithRawBody<User>> getUser();
}
class User {
String name;
@Override public String toString() {
return "User[name=" + name + "]";
}
}
final class WithRawBodyMain {
public static void main(String[] args) throws IOException {
MockWebServer server = new MockWebServer();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(server.url("/"))
.addConverterFactory(new WithRawBodyConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build();
ExampleService service = retrofit.create(ExampleService.class);
server.enqueue(new MockResponse().setBody("{\"name\":\"Jake\"}"));
WithRawBody<User> user = service.getUser().execute().body();
System.out.println(user.body());
System.out.println(user.raw().string());
server.shutdown();
}
}
This prints:
User[name=Jake]
{"name":"Jake"}
What kind of issue is this?
[ ] Question. This issue tracker is not the place for questions. If you want to ask how to do something, or to understand why something isn't working the way you expect it to, use Stack Overflow. https://stackoverflow.com/questions/tagged/retrofit
[ ] Bug report. If you’ve found a bug, spend the time to write a failing test. Bugs with tests get fixed. Here’s an example: https://gist.github.com/swankjesse/6608b4713ad80988cdc9
[ x] Feature Request. Start by telling us what problem you’re trying to solve. Often a solution already exists! Don’t send pull requests to implement new features without first getting our support. Sometimes we leave features out on purpose to keep the project small.
Given:
Currently the code in OkHttpCall.parseResponse(rawResponse) looks like this:
Scenario: I have a piece of software that is executing calls against a web service and getting responses as JSON and deserializing these.
I need a way to capture the original JSON body (string content) for use by another software system.
Unfortunately by the time the response body has been parsed it is no longer possible to get the body as the raw response is now a NoContentResponseBody whose string body is unavailable. See in order to write simpler code without callbacks in the context of coroutines.
OkHttpCall.parseResponse(rawResponse)
. My program is building apis that return ResponseWould it be possible to define a ResponseBody type that just contains the original response body as a string for use later (rather than the current NoContentResponseBody that is generated in
OkHttpCall.parseResponse(rawResponse)
)? Would it be possible to have a parameter that could be passed to Retrofit.Builder specifying we want this kind of behaviour in our calls?Thank you