I am using Retrofit 2 in my Android project. When I hit an API endpoint using a GET method and it returns a 400 level error I can see the error content when I use an HttpLoggingInterceptor, but when I get to the Retrofit OnResponse callback the error body's string is empty.
I can see that there is a body to the error, but I can't seem to pull that body when in the context of the Retrofit callback. Is there a way to ensure the body is accessible there?
Thanks, Adam
Edit: The response I see from the server is: {"error":{"errorMessage":"For input string: \"000001280_713870281\"","httpStatus":400}}
I am trying to pull that response from the response via:
BaseResponse baseResponse = GsonHelper.getObject(BaseResponse.class, response.errorBody().string());
if (baseResponse != null && !TextUtils.isEmpty(baseResponse.getErrorMessage()))
error = baseResponse.getErrorMessage();
(GsonHelper is just a helper which passes the JSON string through GSON to pull the object of type BaseResponse
)
The call to response.errorBody().string() results in an IOException: Content-Length and stream length disagree, but I see the content literally 2 lines above in Log Cat
I encountered the same problem before and I fixed it by using the code response.errorBody().string() only once. You'll receive the IOException if you are using it many times so it is advised to just use it as a one-shot stream just as the Documentation on ResponseBody says.
My suggestion is: convert the Stringified errorBody() into an Object immediately as the latter is what you're gonna be using on subsequent operations.
As it was mentioned, you need to use response.errorBody().string()
only once. But there is a way to get the error body string more than once.
TL;DR Use the code below to get error body string from response
more than once.
public static String getErrorBodyString(Response<?> response) throws IOException {
// Get a copy of error body's BufferedSource.
BufferedSource peekSource = response.errorBody().source().peek();
// Get the Charset, as in the original response().errorBody().string() implementation
MediaType contentType = response.errorBody().contentType(); //
Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;
charset = Util.bomAwareCharset(peekSource, charset);
// Read string without consuming data from the original BufferedSource.
return peekSource.readString(charset);
}
Explanation:
This is based on the original response.errorBody().string()
method implementation. It uses the copy of BufferedSource
from peek()
and returns the error body string without consuming it, so you can call it as many times as you need.
If you look at the response.errorBody().string()
method implementation, you'll see this:
public final String string() throws IOException {
try (BufferedSource source = source()) {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
}
}
source.readString(charset)
consumes data of the error body's BufferedSource
instance, that's why response.errorBody().string()
returns an empty string on next calls.
To read from error body's BufferedSource
without consuming it we can use peek()
, which basically returns a copy of the original BufferedSource
:
Returns a new BufferedSource that can read data from this BufferedSource without consuming it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With