How to get data from Response Header using Retrofit,okHttp client [duplicate] - android

I am using Retrofit with the OkHttp Client and Jackson for Json Serialization and want to get the header of the response.
I know that i can extend the OkClient
and intercept it. But this comes before the deserialization process starts.
What i basically needs is to get the header alongside with the deserialized Json Object.

With Retrofit 1.9.0, if you use the Callback asynchronous version of the interface,
#GET("/user")
void getUser(Callback<User> callback)
Then your callback will receive a Response object
Callback<User> user = new Callback<User>() {
#Override
public void success(User user, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
}
Which has a method called getHeaders()
Callback<User> user = new Callback<User>() {
#Override
public void success(User user, Response response) {
List<Header> headerList = response.getHeaders();
for(Header header : headerList) {
Log.d(TAG, header.getName() + " " + header.getValue());
}
}
For Retrofit 2.0's interface, you can do this with Call<T>.
For Retrofit 2.0's Rx support, you can do this with Observable<Result<T>>

In Retrofit 2.0.0, you can get header like this:
public interface Api {
#GET("user")
Call<User> getUser();
}
Call<User> call = api.getUser();
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
// get headers
Headers headers = response.headers();
// get header value
String cookie = response.headers().get("Set-Cookie");
// TODO
}
#Override
public void onFailure(Call<User> call, Throwable t) {
// TODO
}
});

Much like you I wanted the headers along side of the payload. I needed access to the Etag. It takes some retro-foo, but you can do it. here's what I did. It's a dirty sample so dont take this as a best practices sample.
public static RestAdapter.Builder getRestBuilder(Context context) {
GsonBuilder gsonBuilder = GsonBuilderUtils.getBuilder();
Gson gson = gsonBuilder.create();
// **
// 1. create our own custom deserializer here
// **
final MyGsonConverter gsonConverter = new MyGsonConverter(gson);
OkHttpClient httpClient = MyPersonalOkHttpFactory.getInstance().getAuthHttpClient(context);
httpClient.networkInterceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Response response = chain.proceed(originalRequest);
// **
// 2. add the headers from the Interceptor to our deserializer instance
// **
gsonConverter.headers = response.headers();
return response;
}
});
RestAdapter.Builder builder = new RestAdapter.Builder()
.setClient(new OkClient(httpClient))
.setEndpoint(Common.getApiOriginUrl())
.setConverter(gsonConverter);
return builder;
}
private static class MyGsonConverter extends GsonConverter {
private Headers headers;
public MyGsonConverter(Gson gson) {
super(gson);
}
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
Object obj = super.fromBody(body, type);
// **
// 3. at this point, gson is called and you have access to headers
// do whatever you want here. I just set it on the return object.
// **
if (obj instanceof HeadersArrayList) {
((HeadersArrayList)obj).setHeaders(headers);
}
return obj;
}
}
public class HeadersArrayList<K> extends ArrayList<K>{
private Headers headers;
public Headers getHeaders() {
return headers;
}
public void setHeaders(Headers headers) {
this.headers = headers;
}
}
// the retrofit api for reference
#GET("/api/of/my/backend/{stuff}")
HeadersArrayList<String> getSomething(#Path("stuff") String stuff);

First print the entire response, body, code, message, header(by logging or something else) and try to find a clue from there.
I would recommend you to read the API docs and see the type of request it is asking for.
Use Postman to check which one of the following is working:
1.form-data
2.x-www-form-Urlencoded
3.raw
4.binary
And then accordingly set the annotations in the method declarations in the interface.
eg: in my case, it was taking x-www-form-Urlencoded so I had to mention it using
#FormUrlEncoded
#Headers("Content-Type: application/x-www-form-urlencoded")
in the method declaration.
Then used #Field annotations for individual value I was sending
like
Call<'ReturnObj'> Signup(#Field("name") String name, #Field("phoneNumber") long phoneNumber, #Field("password") String password, #Field("counter") int counter);

Related

Retrofit Headers Not Working Issue

I am using Postman to hit server GET call with url. and adding headers like below in headers sections in postman so when run in postman it works and send data in body but In android using Retrofit not working with 401 error.
X-APIClient: {"apiClientId":"testing-account-cli","apiToken":"$2y$10$C/quaRQUsrWa30hjQJuckOXbW9kIZ.W3G1TlLMYg6lr/XDUes7SM."}
X-Header-Request: {"deviceId":"ffffffff-daac-6513-4eca-0c41298e00df"}
And It works on Postman. But In Android with Retrofit, it's not working 401 error.
1)
#GET("user-list")
Call<User> getUsers(#HeaderMap Map<String, String> headers);
2)
public static Map<String,String> addCustomHeaders()
{
headers = new HashMap<>();
headers.put("X-APIClient",
"\"apiClientId\":\"testing-account-cli\",\"apiToken\":\"$2y$10$C/quaRQUsrWa30hjQJuckOXbW9kIZ.W3G1TlLMYg6lr/XDUes7SM.");
headers.put("X-Header-Request", "\"deviceId\":\"ffffffff-daac-6513-4eca-0c41298e00df")
;
return headers;
}
3) Calling GET using below fails always
Call<User> call = api.getUsers(RetrofitClient.addCustomHeaders());
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
Log.i(TAG, "GET User Success." + response.body().toString());
}
#Override
public void onFailure(Call<User> call, Throwable t) {
Log.i(TAG, "GET Failed Users." + t.getMessage());
}
});
Please Help whats wrong. As its always throwing 401 with authentication false and authorization false.
Try function below, I separated headers.
public static Map<String,String> addCustomHeaders()
{
HashMap<String, String> headers = new HashMap<>();
headers.put("apiClientId" ,"testing-account-cli");
headers.put("apiToken", "$2y$10$C/quaRQUsrWa30hjQJuckOXbW9kIZ.W3G1TlLMYg6lr/XDUes7SM.");
headers.put("deviceId","ffffffff-daac-6513-4eca-0c41298e00df");
return headers;
}
Use interceptor for header
public class HeaderInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
request = request.newBuilder()
.header("apiClientId", "testing-account-cli")
.header("apiToken","$2y$10$C/quaRQUsrWa30hjQJuckOXbW9kIZ.W3G1TlLMYg6lr/XDUes7SM.")
.header("deviceId","ffffffff-daac-6513-4eca-0c41298e00df")
.build()
Response response = chain.proceed(request);
return response;
}
//add this class into retrofit class like
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new HeaderInterceptor())

Android Retrofit adding shared preference value in end point

I am new in using retrofit in android. I have a get request which is working in a good manner but I want to include the value of shared preference in the endpoint of the URL. Suppose my end point Url is :
public interface Data{
#GET("/myphone/extra/pull/Sharedpreferencevalue") //add shared preference value here
}
Can I do this in retrofit or I have to do with some other way? or how it can be done in retrofit?
You can add parameters dynamically as follows:
#GET("/myphone/extra/pull/{Sharedpreferencevalue}")
Call<YourResponseClass> getData(#Path("Sharedpreferencevalue") String value);
You can use #Path annotation to programmatically add value to the endpoint, and do something like this in your with retrofit Service interface:
#GET("/myphone/extra/pull/{sharedprefValue}")
Call<EntityName> getPref(#Path("sharedprefValue") String pref);
Use urls dynamically to retrofit2 as follows.
in your interface
#GET
public Call<ResponseBody> fetchMileage(#Url String url);
use it this way
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(ROOT_URL)
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.client( httpClient.build()).build();
MyInterface myInterface = retrofit.create(MyInterface.class);
Call<ResponseBody> result = myInterface.fetchMileage(endpointUrl);
result.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
String output ="";
if (response.isSuccessful()) {
try {
output = response.body().string();
}catch (IOException e)
{
e.printStackTrace();
}
}else{
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable) {
//Toast.makeText(context,"Error "+throwable.getMessage(),Toast.LENGTH_LONG).show();
throwable.printStackTrace();
}
});

OkHttp doesn't redirect POST requests when used with retrofit

Using retrofit I want to make POST request to http://milzinas.lt/oauthsilent/authorize. This URL is special because it redirects you to http://milzinas.e-bros.lt/oauthsilent/authorize. My retrofit setup uses OkHttpClient. If I make request using OkHttpClient only then redirecting works fine, i.e. 401 status code is received. However, when I use the same OkHttpClient with retrofit then response is status code 307. I think this has something to do with OkClient implementation which wraps the OkHttpClient but I'm not sure. Below is the code I used to test this scenario. I'm using these libraries:
com.squareup.retrofit:retrofit:1.9.0
com.squareup.okhttp:okhttp:2.2.0
I understand that when URL is redirecting you to another URL the http client has to make two requests. In my case the first request returns 307 (Temporary Redirect) and the second one returns 401 (Unauthorized). However, retrofit always returns response of the first request. Do you know how to make redirecting work properly with retrofit? Maybe I could achieve this by using some other HTTP client? Any suggestions will be appreciated.
So when I execute code below console prints
Retrofit failure. Status: 307
OkHttp. Status: 401
I want it to be
Retrofit failure. Status: 401
OkHttp. Status: 401
public class MainActivity extends AppCompatActivity {
interface Api {
#POST(URL)
#Headers("Accept: application/json")
void test(#Body Object dummy, Callback<Object> callback);
}
static final String BASE_URL = "http://milzinas.lt";
static final String URL = "/oauthsilent/authorize";
final OkHttpClient okHttpClient = new OkHttpClient();
Api api;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RestAdapter retrofit = new RestAdapter.Builder()
.setEndpoint(BASE_URL)
.setClient(new OkClient(okHttpClient))
.setConverter(new Converter() {
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
return null;
}
#Override
public TypedOutput toBody(Object object) {
return null;
}
})
.build();
api = retrofit.create(Api.class);
makeRequestOkHttp();
makeRequestRetrofit();
}
void makeRequestOkHttp() {
new AsyncTask<Object, Object, Object>() {
#Override
protected Object doInBackground(Object... objects) {
try {
Request request = new Request.Builder().url(BASE_URL + URL).build();
com.squareup.okhttp.Response response = okHttpClient.newCall(request).execute();
android.util.Log.d("matka", "OkHttp. Status: " + response.code());
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
}.execute();
}
void makeRequestRetrofit() {
api.test("", new Callback<Object>() {
#Override
public void success(Object o, Response response) {
android.util.Log.d("matka", "Retrofit success. Status: " + response.getStatus());
}
#Override
public void failure(RetrofitError error) {
android.util.Log.d("matka", "Retrofit failure. Status: " + error.getResponse().getStatus());
}
});
}
}
The problem persist even in the latest v3.5.0
The only workaround that works is
https://github.com/square/okhttp/issues/936#issuecomment-266430151

Receiving an empty body in retrofit Response

I am using retrofit to get data from http URL.
My Interface Class :
public interface SlotsAPI {
/*Retrofit get annotation with our URL
And our method that will return a Json Object
*/
#GET(url)
retrofit.Call<JSONObject> getSlots();
}
My request method.
public void getResponse(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
//Creating an object of our api interface
SlotsAPI api = retrofit.create(SlotsAPI.class);
retrofit.Call<JSONObject> callback = api.getSlots();
callback.enqueue(new Callback<JSONObject>() {
#Override
public void onResponse(Response<JSONObject> response) {
if (response != null) {
Log.d("OnResponse", response.body().toString());
}
}
#Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
}
In the response I am receiving an empty body.And the server responds with 200 OK.
D/OnResponse: {}
But when I open the URL in browser I am getting JSONObject on the screen.
you should try like this way ....
public interface SlotsAPI {
/*Retrofit get annotation with our URL
And our method that will return a Json Object
*/
#GET(url)
Call<JsonElement> getSlots();
}
in request method
retrofit.Call<JsonElement> callback = api.getSlots();
callback.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Response<JsonElement> response) {
if (response != null) {
Log.d("OnResponse", response.body().toString());
}
}
Please check your JsonObject. If you want to get response in json you must be define a response type JsonObject not JSONObject other wise specify the pojo class in your interface.
I think you are not understanding the retrofit filosofy.
The correct interface should be:
public interface SlotsAPI {
/*Retrofit get annotation with our URL
And our method that will return a Json Object
*/
#GET(url)
JSONObject getSlots();
}
When you call the getSlots method, retrofit will automatically do the HTTP request and return the JSONObject.
You will need to do this out of the main thread.
Make sure that the url of #Get is relative path
#Base URL: always ends with /
#Url: DO NOT start with /
Example:
String URL = http://api.co/base/ ;
And
#GET("webservice/syncdown")
JSONObject getSlots();
You may receiving a list of Slots. the Gson converter will handle it if you sending array of json
#GET(url)
retrofit.Call<List<Slot>> getSlots();
You are using the retrofit 2 or 1? The version 2 still is in beta.
If you are using the version 1. Use this:
public interface SlotsAPI {
/*Retrofit get annotation with our URL
And our method that will return a Json Object
*/
#GET(url)
void getSlots(Callback<JsonElement> callback);
}
With this the call will be asynchronous.
Same problem here, and answer from curiousMind saved my day.
More on the same subject: if you need to get a value from a pair use:
String value = response.body().getAsJsonObject().get("pair_name").getAsString();
Call<Void> getSlots() worked for me.
private void APIRetrofit_method() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(RecyclerInterface.JSONURL)
// .client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
RecyclerInterface api = retrofit.create(RecyclerInterface.class);
Call<ResponseBody> call = api.getString(); /// GET METHOD without passing params
// Post METHOD CODE START
// HashMap<String, String> params = new HashMap<String, String>();
// params.put("name", "yuva");
// params.put("pass", "" + "123");
// Call<ResponseBody> call1 = api.getProspectList(params);
// Post METHOD CODE END
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
Log.d(TAG, "GetProspectlistresponse" + "" + response.isSuccessful());
utility.hideProgressDialog();
if (response.isSuccessful()) {
String remoteResponse = new String(response.body().string());
Log.d(TAG, "Holidaylistresponse" + "" + remoteResponse);
try {
JSONObject object = new JSONObject(remoteResponse);
JSONArray array = object.getJSONArray("Holidays_Details");
if (array.toString().equals("[]")) {
holiday_recyclerView.setVisibility(View.GONE);
} else {
holiday_recyclerView.setVisibility(View.VISIBLE);
for (int i = 0; i < array.length(); i++) {
JSONObject c = array.getJSONObject(i);
String holidayDate = c.getString(TAG_HOLIDAYDATE);
String holidayName = c.getString(TAG_HOLIDAYName);
String holidaytype = c.getString(TAG_HOLIDAYtype);
HashMap<String, String> customers = new HashMap<String, String>();
customers.put(TAG_HOLIDAYDATE, holidayDate);
customers.put(TAG_HOLIDAYName, holidayName);
customers.put(TAG_HOLIDAYtype, holidaytype);
arrayList.add(customers);
}
getHolidaylistAdapter.notifyDataSetChanged();
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
utility.hideProgressDialog();
}
} catch (IOException e) {
e.printStackTrace();
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i("ErrorResponsestring", call.toString());
}
});
}
String JSONURL = "https://demonuts.com/Demonuts/JsonTest/Tennis/";
#GET("json_parsing.php")
Call<ResponseBody> getString();
// #POST("getProspectList")
// #FormUrlEncoded
// Call<ResponseBody> getProspectList(#FieldMap HashMap<String, String> body);
implementation 'com.squareup.retrofit2:retrofit:2.0.2'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
implementation 'com.squareup.okhttp3:okhttp:4.0.0'

Reddit oAuth 2 for Android "userless" app with Retrofit

I'm trying to implement the Reddit oAuth2 (every app that utilizes Reddit content has to have this implemented) in Android based 'userless' application and I'm following the guidelines.
I registered an app and get the respective client_id.
I'm following this for API guidelines and this for Retrofit in order to properly write the Android code.
Hence, I've coded two approaches to the issue and it seems that neither works. The call in the appropriate Fragment is the same for the two options and it goes as follows:
public void oAuth(){
String bodyString = "grant_type=" + "https://oauth.reddit.com/grants/installed_client"
+ "&device_id=" + UUID.randomUUID().toString();
TypedInput requestBody = new TypedByteArray("application/x-www-form-urlencoded", bodyString.getBytes(Charset.forName("UTF-8")));
RedditAPI.sRedditAuth().redditAuth(requestBody, new Callback<TokenResponse>() {
#Override
public void success(TokenResponse tokenResponse, Response response) {
Log.d("OATH_TAG", "oAuth() | YAY! :)");
}
#Override
public void failure(RetrofitError error) {
Log.d("OATH_TAG", "oAuth() | NOOOOOoooooo.... :(");
}
});
}
OPTION 1:
the Retrofit interface:
public interface RedditAuthInterface {
#POST(Urlz.REDDIT_OATH2_PATH)
void redditAuth(#Body TypedInput body, Callback<TokenResponse> result);
}
//the adapter
public static RedditAuthInterface sRedditAuth() {
if (sRedditAuthInterface == null) {
RestAdapter restAdapter = new RestAdapter
.Builder()
.setClient(getAuthClient())
.setEndpoint(Urlz.BASE_REDDIT_URL)
.build();
sRedditAuthInterface = restAdapter.create(RedditAuthInterface.class);
}
return sRedditAuthInterface;
}
/* support methods */
private static OkClient getAuthClient() {
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(Static.READ_TIMEOUT, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(Static.CONNECT_TIMEOUT, TimeUnit.SECONDS);
/*okHttpClient.setAuthenticator(new Authenticator() {
#Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
String credential = Credentials.basic(BldCnfg.REDDIT_CLIENT_ID, BldCnfg.REDDIT_PASS);
return response.request().newBuilder().header("Authorization", credential).build();
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
return null;
}
});*/
okHttpClient.networkInterceptors().add(OAUTH_INTERCEPTOR);
return new OkClient(okHttpClient);
}
private static final Interceptor OAUTH_INTERCEPTOR = new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
String credentials = BldCnfg.REDDIT_CLIENT_ID + ":" + BldCnfg.REDDIT_PASS; // REDDIT_PASS = "" as by API guides
String string = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
originalResponse.header("Authorization", string);
originalResponse.header("Accept", "application/json");
return originalResponse;
}
};
result:
RetrofitError: 401 Unauthorized
OPTION 2:
the Retrofit interface:
public interface RedditAuthInterface {
#POST(Urlz.REDDIT_OATH2_PATH)
void redditAuth(#Body TypedInput body, Callback<TokenResponse> result);
}
//the adapter
public static RedditAuthInterface sRedditAuth() {
if (sRedditAuthInterface == null) {
RestAdapter restAdapter = new RestAdapter
.Builder()
.setClient(getConfuguredClient())
.setRequestInterceptor(getRequestInerceptorPass())
.setEndpoint(Urlz.BASE_REDDIT_URL)
.build();
sRedditAuthInterface = restAdapter.create(RedditAuthInterface.class);
}
return sRedditAuthInterface;
}
/* support methods */
public static RequestInterceptor getRequestInerceptorPass() {
RequestInterceptor rqInter = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
String credentials = BldCnfg.REDDIT_CLIENT_ID + ":" + BldCnfg.REDDIT_PASS; // REDDIT_PASS = "" as by API guides
String string = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
request.addHeader("Authorization", string);
request.addHeader("Accept", "application/json");
}
};
return rqInter;
}
private static OkClient getConfuguredClient() {
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(Static.READ_TIMEOUT, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(Static.CONNECT_TIMEOUT, TimeUnit.SECONDS);
return new OkClient(okHttpClient);
}
result:
It seems that I'm getting empty response (I only get "*" for scope). The successful response looks like this:
and header like this:
Do you have any ideas what am I doing wrong?
Has anybody done this?
The official Reddit github wiki lacks Android examples (has in almost every other language, though).
I was going through the same problem before and make this library to handel OAuth2 in Android. and the library is an extension for Retrofit that simplifies the process of authenticating against an OAuth 2 provider.
Based on your image with the "empty" response, showing that you got * back as a scope, I suspect that your definition for the access token response is using camel case instead of snake case, so the JSON is not getting loaded properly into the Java object.

Categories

Resources