I'm using ACRA to report exceptions and would like to include the last API call (and potentially the response body) as part of the custom data that can be supplied. I've been looking over the available RestAdapter.Builder interfaces but don't see one that supplies what I'm looking for. Effectively, I'd like access to what's put into Logcat when full logging is enabled.
Is there a way to get the last URL, headers, and response body within Retrofit so I can set custom ACRA fields?
Retrofit allows specifying a Profiler which gets invoked immediately before and after requests are made.
public interface Profiler<T> {
T beforeCall();
void afterCall(RequestInformation info, long elapsedTime, int statusCode, T beforeData);
}
While you don't get access to the actual body, you get a good bit of information about it.
Related
The Premise
I'm working on a simple app where I want to list out a user's GitHub repos in a RecyclerView. I'm using this as my endpoint while building this.
The Problem
The problem I'm facing is that the GitHub API returns only 30 repos in one go. To get more, I can append a per_page=100 (100 is the maximum) to my query string; But, what do we do about users with more than 100 repos?
The solution the API docs provide is to get the next; url from the "Link" response header to make a second API call.
How does one go about this? Thanks!
The Response class will let you access the headers. The GitHub client would have a method like:
#GET("/search/code")
Observable<Response<T>> getUser(...)
Inside onNext(), you'd do something like:
#Override
public void onNext(Response<T> response) {
String next = response.headers().get("next");
}
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.
I use ion library for downloading files in my app. I want to read the response headers, check a particular header and either cancel or continue the download. Of course I can execute the HEAD query, check the header, and then execute the GET query. But I want to execute only one request.
Is there a way to get a callback when received all the headers, handle them and either continue or cancel the download?
Use the onHeaders callback.
.onHeaders(...)
https://github.com/koush/ion/blob/master/ion/src/com/koushikdutta/ion/builder/RequestBuilder.java#L186
I found another solution. Maybe it's better?
Ion.getDefault(<Context>).getHttpClient().insertMiddleware(new SimpleMiddleware()
{
#Override
public void onHeadersReceived(OnHeadersReceivedDataOnRequestSentData data)
{
super.onHeadersReceived(data);
}
});
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.
I am using Android Annotation in my project and trying to send POST request through following code, however there is something wrong in following code as I am not getting response as expected:
#Rest(rootUrl = "http://xyz.com", converters = {GsonHttpMessageConverter.class})
public interface A {
#Post("/authenticate/email/")
public Object attemptLogin(Map data);
}
Where data is (key, value) pair. Is there anything I am missing perhaps Do I have to set request-header or data should not be JSON?
I found the solution from Rest client using Android-Annotations.
Like the GET requests, it is extremely simple to send POST requests using Android-Annotations. One difference is that you need to define the parameters that you are going to send as a custom class (e.g. Event class in the example below) or if you want to control this dynamically, then a Map (e.g. a MultiValueMap). The url for the request can still be constructed in a similar fashion using the variables enclosed inside {...} and the response can be handled similarly as in GET requests.
#Post("/events")
void addEvent(Event event);
#Post("/events/{id}")
void addEventById(Event event, long id);
#Post("/authenticate/event/")
Object authenticateEventData(MultiValueMap data);