How to handle non-2XX response codes in RoboSpice? - android

In my API server returns HTTP 400 response code if request does not pass validation, and provides detailed message, that should be parsed as the response.
For example:
public class RegistrationResponse {
private String emailError; // Detailed message. Null if no error occured
}
But Robospice (Retrofit + OkHttp) fires onRequestFailure() with message "retrofit.RetrofitError: 400 BAD REQUEST" in this case and, of course, does not parse anything.
How should I make it parse the response in case if response code is not 2XX?

You should declare Retrofit methods that return HTTP Response objects and check the raw object in your loadDataFromNetwork() for the status you need. This way, however, you will skip the out-of-the-box functionality of parsing responses and will have to do that manually.
Therefore, you should also find a way to reuse the Converter passed to your RestAdapter in the RetrofitSpiceService. Overriding the RetrofitSpiceService#createConverter() method is probably the simplest way to achieve this.

Related

How to consume different response bodies based on the status code using Retrofit?

My Android app that uses Retrofit for REST calls needs to consume different response types for the same endpoint call, depending on the response code. For example:
If I send a request to GET /api/user/1 and the HTTP status code is 200 then I should get a response body like this:
{
"name": "John",
"age": 30
}
But if I send a request to GET /api/user/1 and the HTTP status code is 404 then I should get a response body like this:
{
"errorCode": "UserNotFound"
}
But if I send a request to GET /api/user/1 and the HTTP status code is 429 then I should get a response body like this:
{
"maxRequestsPerHour": 60,
"requestsRemaining": 0,
"newRequestAvailableAt": "2020-02-08T09:30:26Z"
}
I found some people online suggesting to create a Retrofit adapter to deserialize a successful response (between 0 to 200 range) and an error for everything else, like this:
public interface ApiService {
#GET("users/{userId}")
Call<NetworkResponse<User, Error>> getUser(#Path("userId") String userId);
}
And this could work if I had only two types of possible response, a User response and an Error response, but the API that I'm trying to consume has multiple response bodies for the same endpoint, depending on the status code.
In the example above I showed 3 possible responses, but some endpoints have 5 different response types (with different fields), depending if the response is 200, 404, 409, 500, etc... I don't want to create an adapter on Retrofit to handle each possible response like this:
public interface ApiService {
#GET("users/{userId}")
Call<NetworkResponse<User, ErrorNotFound, ErrorTooManyRequest, ServerError, WhateverError>> getUser(#Path("userId") String userId);
}
It doesn't look good, in my opinion.
So I have two questions:
Is there a more elegant way to handle multiple (more than 2) response types to the same endpoint, depending on the status code of the response?
What are your thoughts on this REST API design approach? I have seen REST APIs before that return basically two response types, when request is successful and when the request fails for some reason, but the response when the request fails has always the same format (same number of fields), but this is the first time I see a REST API with multiple response formats for error, with completely different fields for each type of error.

Is it possible to programmatically parse an OkHttp response into a RetrofitError?

I don't see how to do it. To even call RetrofitError.httpError(), you need a Retrofit.client.Response instead of the OkHttp response, which is what we get.

Android Retrofit 2, differences between addInterceptor & addNetworkInterceptor for editing responses

I've been trying to implement an interceptor ( OkHttp 3.2 & Retrofit 2 ) for editing the JSON response before is returned as response. The server we request data returns different data dependes on success or error and that makes difficult to map the objects.
I was trying to do it by adding the interceptor to Retrofit as a NetworkInterceptor, however the string returned had no format.
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
try {
final String responseString = new String(response.body().bytes() );
LOGD("OkHttp-NET-Interceptor", "Response: " + responseString);
String newResponseString = editResponse( responseString );
LOGD("OkHttp-NET-Interceptor", "Response edited: " + newResponseString);
return response.newBuilder()
.body(ResponseBody.create(response.body().contentType(), newResponseString))
.build();
}catch (Exception ex){
return response;
}
}
responseString had a string without any understandable format.
After changing to the normal interceptor, the string had format a it was able to convert to JSONObject.
Could tell me someone which are the differences between the responses?
why this line new String(response.body().bytes() ); return different content?
The differences are in the names. NetworkInterceptor hooks in at the network level and is an ideal place to put retry logic and anything that doesn't rely on the actual content of the response.
If what you do depends on the contents of the response (like in your case), using a ApplicationInterceptor is more useful, as it gives you the response after it's been processed by any other moving parts you may have such as a JSON deserializer. Otherwise you would have to implement the JSON deserializing yourself inside the NetworkInterceptor which doesn't make much sense considering it's done for you by Retrofit.
Clarification
Square have this useful diagram on their wiki that shows where each type of interceptor sits
Thus, the reason you receive a readable string in the ApplicationInterceptor is because Square are trying to de-couple the purposes of the two interceptor types. They don't think you should be making any application dependent decisions in the NetworkInterceptor, and so they don't provide an easy way for you to access the response string. It is possible to get ahold of, but like I said, they don't want you to make decisions that depend on the content of the response - rather, they want you to make decisions based or the network state, or headers etc.
The ApplicationInterceptor is where they want you to make decisions dependent upon the contents of the response, so they provide easier methods to access the content of the response so that you can make informed decisions to retry, or as they detail in their wiki, rewrite responses (which I believe is what you're trying to do).
According to #square:
Each interceptor chain has relative merits.
Application interceptors
Don’t need to worry about intermediate responses like redirects and retries.
Are always invoked once, even if the HTTP response is served from the cache.
Observe the application’s original intent. Unconcerned with OkHttp-injected headers like If-None-Match.
Permitted to short-circuit and not call Chain.proceed().
Permitted to retry and make multiple calls to Chain.proceed().
Can adjust Call timeouts using withConnectTimeout, withReadTimeout, withWriteTimeout.
Network Interceptors
Able to operate on intermediate responses like redirects and retries.
Not invoked for cached responses that short-circuit the network.
Observe the data just as it will be transmitted over the network.
Access to the Connection that carries the request.

Retrofit/Robospice: get response headers from successful request?

I am using Retrofit/Robospice to make api calls in an app I've built, with a RetrofitGsonSpiceService. All responses are converted into POJOs using a GSON converter, however there is some information I need to retrieve from the response header. I cannot find any means to get the headers (I can only get the headers if the request is unsuccessful because the raw response is sent in the error object!) how can I intercept the response to grab the headers before it is converted?
It took me a few minutes to figure out exactly what #mato was suggesting in his answer. Here's a concrete example of how to replace the OkClient that comes with Retrofit in order to intercept the response headers.
public class InterceptingOkClient extends OkClient
{
public InterceptingOkClient()
{
}
public InterceptingOkClient(OkHttpClient client)
{
super(client);
}
#Override
public Response execute(Request request) throws IOException
{
Response response = super.execute(request);
for (Header header : response.getHeaders())
{
// do something with header
}
return response;
}
}
You then pass an instance of your custom client to the RestAdapter.Builder:
RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new InterceptingOkClient())
....
.build();
RoboSpice was designed in a way it doesn't know anything about the HTTP client you end up using in your app. That being said, you should get the response headers from the HTTP client. As Retrofit may use Apache, OkHttp or the default Android HTTP client, you should take a look and see which client you are currently using. Take into account that Retrofit chooses the HTTP client based on certain things (please refer to the Retrofit documentation, or dig into the code, you will find it), unless you manually specify it.
Retrofit defines an interface for clients called Client. If you take a look at the source code, you will see that three classes implement this interface: ApacheClient, OkClient and UrlConnectionClient. Depending on which of them you want to use, extend from one of those, and try to hook into the code that is executed when a response comes back, so that you can get the headers from it.
Once you do that, you have to set your custom Client to Retrofit.

Android Volley Caching - Get Response by URL and Request Body

From my experimenting so far, it appears that Volley accesses its cache based on the URL alone. I would like to know if it also can determine which response it should return based on the combination of URL and request body data.
Is there a setting for this, or and extension point where I can implement this myself.
I don't see anywhere that the request body data is returned in the NetworkResponse object, or I would do the check in the parseNetworkResponse method.
Thanks
You have to override Request.getCacheKey() method to include request body as well, when implementing your custom request.

Categories

Resources