In a not so exotic situation I need access to the original request when I handle the response in the responseBodyConverter. For example, right now I need the original url -- e.g. I want to adjust relative urls in the response HTML. And perhaps I will need some request header, like a cookie.
But I don't know how to get the request data.
Here is what I have:
HttpLoggingInterceptor hlog = ses.http_logger(servername);
if (hlog != null) {
builder.addInterceptor(hlog);
}
OkHttpClient okhttpClient = builder
.build();
return new Retrofit.Builder()
.baseUrl(base_url)
.client(okhttpClient)
.addConverterFactory(
new Converter.Factory() {
#Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
return responseBody -> {
BufferedSource buffer = Okio.buffer(responseBody.source());
String html = buffer.readUtf8();
System.out.println(html);
};
}
})
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
;
I found this solution https://github.com/square/retrofit/issues/2267/
But I can not understand what it does or whether it is too complex or any side effects.
Can I get access to the request data in Retrofit2 while processing the response in responseBodyConverter and how?
Edit: the above works as expected, but a simpler way is preferred.
Related
Problem Statement:
I'm using Retrofit in my application for API calls. Currently I've 20+ Retrofit Interfaces, with different Callbacks. Currently when app receives INVALID_SESSION_ID in anyone of these Interfaces (say UpdateUserAPI), I've to get new ACCESS_TOKEN, by invoking AccessTokenAPI.
Approach Suggested:
When app receives INVALID_SESSION_ID in Callback in UpdateUserAPI, invoke AccessTokenAPI to get new ACCESS_TOKEN. Upon receiving new ACCESS_TOKEN, post the actual call (with initial parameters in UpdateUserAPI) with new ACCESS_TOKEN. But this requires to save parameters in the class which implements UpdateUserAPI. Also I need to retry getting ACCESS_TOKEN only once, which should be handled.
What is the best approach to implement above requirement?
Create your own TokenInterceptor
public class TokenInterceptor implements Interceptor
Then set it to your okktpclient
Interceptor tokenInterceptor = new TokenInterceptor(provideUserLoginDao(appDatabase));
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(tokenInterceptor)
.writeTimeout(50, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
Useful information in this post also : Refreshing OAuth token using Retrofit without modifying all calls
Create your own custom interceptor and check your token/session_id is valid or not. If your session_id is expired and then hit your updateUserAPI to get new id and set this id in header or where you want. Here is some code samples.
RefreshTokenInterceptor
public static class RInterceptor implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
try {
if (response.code() == 410) {
Response r = null;
try {
r = makeTokenRefreshCall(request, chain);
} catch (JSONException e) {
e.printStackTrace();
}
return r;
}
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
}
private static Response makeTokenRefreshCall(Request req, Interceptor.Chain chain) throws JSONException, IOException {
/* fetch refreshed token, some synchronous API call, whatever Because we are responsible to return new response */
refreshTokenSync();
Request newRequest;
newRequest = req.newBuilder().header("authorization", NEW_TOKEN)/*.post(req.body())*/.build();
return chain.proceed(newRequest);
}
RESTClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(50, TimeUnit.SECONDS)
.writeTimeout(55, TimeUnit.SECONDS)
.connectTimeout(50, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(new NetworkInterceptor())
.build();
My api expects an empty json body ({ }) when making post requests. How do I set this up in Retrofit and Jackson?
I tried passing null, and empty string, and "{}" but could not get this to work.
#POST(my/url)
Call<MyResponse> createPostRequest(#Body Object empty);
How can I set an empty JSON body?
An empty Object does it for Kotlin:
interface ApiService {
#POST("your.url")
fun createPostRequest(#Body body: Any = Object()): Call<YourResponseType>
}
try this . It worked for me now.
#POST(my/url)
Call<MyResponse> createPostRequest(#Body Hashmap );
while using this method pass new HasMap as paremater
apiservice.createPostRequest(new HashMap())
Empty class will do the trick:
class EmptyRequest {
public static final EmptyRequest INSTANCE = new EmptyRequest();
}
interface My Service {
#POST("my/url")
Call<MyResponse> createPostRequest(#Body EmptyRequest request);
}
myService.createPostRequest(EmptyRequest.INSTANCE);
Old question, but I found a more suitable solution by using a okhttp3.Interceptor that adds an empty body if no body is present. This solution does not require you to add an extra parameter for an empty #Body.
Example:
Interceptor interceptor = chain -> {
Request oldRequest = chain.request();
Request.Builder newRequest = chain.request().newBuilder();
if ("POST".equals(oldRequest.method()) && (oldRequest.body() == null || oldRequest.body().contentLength() <= 0)) {
newRequest.post(RequestBody.create(MediaType.parse("application/json"), "{}"));
}
return chain.proceed(newRequest.build());
};
You can then create an instance of your service like so:
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.addInterceptor(interceptor);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("YourURL")
.client(client.build())
.build();
MyService service = retrofit.create(MyService.class);
use:
#POST("something")
Call<MyResponse> createPostRequest(#Body Object o);
then call:
createPostRequest(new Object())
Heres the answer in Kotlin:
#POST("CountriesList")
fun getCountriesNew(#Body body: HashMap<String, String>) : Call<CountryModel>
val call = RetrofitClient.apiInterface.getCountriesNew(HashMap())
I would like to understand how Retrofit works, but the official documentation is very weak.
I need to make a very simple GET request and get the response as a String.
Now I use standard HTTPUrlConnection and it works nicely, just request - response
Can anyone tell me how to get a String response without converting it to an object or something like that?
You can use ScalarsConverterFactory for strings and both primitives and their boxed types to text/plain bodies.
Add this dependency to your build.gradle file:
compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
Try this:
public interface ExampleService {
#GET("/users/{user}/repos")
Call<String> listRepos(#Path("user") String user);
}
And add ScalarsConverterFactory to your builder:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
You can then retrieve this string like this:
Call<String> call = exampleService.listRepos(user);
Response<String> response = call.execute();
String value = response.body();
I am trying to get the raw response using Retrofit2.0.2.
So far I tried to print the response using following line of code but it prints the address and not the exact response body.
Log.i("RAW MESSAGE",response.body().toString());
compile 'com.squareup.retrofit2:retrofit:2.0.2'
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
GitApi gitApi = retrofit.create(GitApi.class);
Call<Addresses> call = gitApi.getFeed(user);
call.enqueue(new Callback<Addresses>() {
#Override
public void onResponse(Response<Addresses> response, Retrofit retrofit) {
try {
mDisplayDetails.setText(response.body().getSuburbs().get(0).getText());
**Log.i("RAW MESSAGE",response.body().toString());**
} catch (Exception e) {
mDisplayDetails.setText(e.getMessage());
}
mProgressBar.setVisibility(View.INVISIBLE);
}
#Override
public void onFailure(Throwable t) {
mDisplayDetails.setText(t.getMessage());
mProgressBar.setVisibility(View.INVISIBLE);
}
});
That's because it's already converted to an object by converter. To get the raw json, you need an interceptor on your Http Client. Thankfully you don't need to write your own class, Square already provide HttpLoggingInterceptor class for you.
Add this on your app-level gradle
compile 'com.squareup.okhttp3:logging-interceptor:3.5.0'
And use it in your OkHttpClient
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor).build();
Don't forget to change your HttpClient in Retrofit.
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl("https://yourapi.com/api/")
.build();
In the Log Cat you'll see the raw json response. Further information is available on Square's OkHttp github.
Caution!
Don't forget to remove Interceptors (or change Logging Level to NONE) in production! Otherwise people will be able to see your request and response on Log Cat.
You have to use "ResponseBody" from okhttp3 in your call. Then, get "response.body().string()" to get the JSONObject as your server gives to you.
It's a good way to catch errors if there are any errors parsing server response to your model object.
Simply use:
Log.i("RAW MESSAGE", response.raw().body().string());
Or:
Log.i("RAW MESSAGE", response.body().string());
Guess you want to see the raw response body for debugging purpose. There are two ways to do this.
Using okhttp3.logging.HttpLoggingInterceptor as #aldok mentioned.
If you want to check some properties of the response object, or convert the json(response body) to POJO manually, you may want to do it like this:
Don't use the addConverterFactory while initializing the retrofit client, comment this line.
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
//.addConverterFactory(GsonConverterFactory.create())
.build();
Then consuming the response like this:
Call<ResponseBody> topRatedResponseCall = apiService.getTopRatedMoves(API_KEY);
topRatedResponseCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
Log.d(LOG_TAG, response.body().string());
int code = response.code();
testText.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
Hope this would help~
I am using the latest okhttp version: okhttp-2.3.0.jar
How to add query parameters to GET request in okhttp in java ?
I found a related question about android, but no answer here!
For okhttp3:
private static final OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
public static void get(String url, Map<String,String>params, Callback responseCallback) {
HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
if (params != null) {
for(Map.Entry<String, String> param : params.entrySet()) {
httpBuilder.addQueryParameter(param.getKey(),param.getValue());
}
}
Request request = new Request.Builder().url(httpBuilder.build()).build();
client.newCall(request).enqueue(responseCallback);
}
Here's my interceptor
private static class AuthInterceptor implements Interceptor {
private String mApiKey;
public AuthInterceptor(String apiKey) {
mApiKey = apiKey;
}
#Override
public Response intercept(Chain chain) throws IOException {
HttpUrl url = chain.request().url()
.newBuilder()
.addQueryParameter("api_key", mApiKey)
.build();
Request request = chain.request().newBuilder().url(url).build();
return chain.proceed(request);
}
}
I finally did my code, hope the following code can help you guys. I build the URL first using
HttpUrl httpUrl = new HttpUrl.Builder()
Then pass the URL to Request requesthttp hope it helps .
public class NetActions {
OkHttpClient client = new OkHttpClient();
public String getStudentById(String code) throws IOException, NullPointerException {
HttpUrl httpUrl = new HttpUrl.Builder()
.scheme("https")
.host("subdomain.apiweb.com")
.addPathSegment("api")
.addPathSegment("v1")
.addPathSegment("students")
.addPathSegment(code) // <- 8873 code passthru parameter on method
.addQueryParameter("auth_token", "71x23768234hgjwqguygqew")
// Each addPathSegment separated add a / symbol to the final url
// finally my Full URL is:
// https://subdomain.apiweb.com/api/v1/students/8873?auth_token=71x23768234hgjwqguygqew
.build();
System.out.println(httpUrl.toString());
Request requesthttp = new Request.Builder()
.addHeader("accept", "application/json")
.url(httpUrl) // <- Finally put httpUrl in here
.build();
Response response = client.newCall(requesthttp).execute();
return response.body().string();
}
}
As mentioned in the other answer, okhttp v2.4 offers new functionality that does make this possible.
See http://square.github.io/okhttp/2.x/okhttp/com/squareup/okhttp/HttpUrl.Builder.html#addQueryParameter-java.lang.String-java.lang.String-
This is not possible with the current version of okhttp, there is no method provided that will handle this for you.
The next best thing is building an url string or an URL object (found in java.net.URL) with the query included yourself, and pass that to the request builder of okhttp.
As you can see, the Request.Builder can take either a String or an URL.
Examples on how to build an url can be found at What is the idiomatic way to compose a URL or URI in Java?
As of right now (okhttp 2.4), HttpUrl.Builder now has methods addQueryParameter and addEncodedQueryParameter.
You can create a newBuilder from existing HttoUrl and add query parameters there. Sample interceptor code:
Request req = it.request()
return chain.proceed(
req.newBuilder()
.url(
req.url().newBuilder()
.addQueryParameter("v", "5.60")
.build());
.build());
Use HttpUrl class's functions:
//adds the pre-encoded query parameter to this URL's query string
addEncodedQueryParameter(String encodedName, String encodedValue)
//encodes the query parameter using UTF-8 and adds it to this URL's query string
addQueryParameter(String name, String value)
more detailed: https://stackoverflow.com/a/32146909/5247331