I am required to pass cookie value in my next service after log in I am using volley library to call services. I have successfully saved cookie using this it successfully saves ids in preference.
i have use getHeader for next request
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return myApp.getSessionCookie();
}
but for my next request how to use this value?
Related
In my andorid app I am making a GET request using Retrofit2:
http://myapi.com/items/list
But I would also like to make another request e.g.
http://myapi.com/items/list/filter/active:true,min_price:100
So the filter parameter is optional. I am trying to do the following:
#GET("items/items/list{filter}")
Observable<ResponseItems> getItems(#Path("filter") String filter);
and calling it like:
service.getItems("")
and:
service.getItems("/filter/active:true,min_price:100")
But it does not work. So I ended up creating two separate service calls, one with filter param and other without. I think that there should be more elegant method though.
So i've seen what you are trying to achieve.
How your api declaration should looks like:
#GET("items/list/{filter}")
Observable<ResponseItems> getItems(#Path(value = "filter", encoded = true) String filter);
and a call service.getItems("") would lead to http://myapi.com/items/list/ be called
a call service.getItems("filter/active:true,min_price:100") would lead to
http://myapi.com/items/list/filter/active:true,min_price:100 be called.
An encoded property in #Path annotation is set because your optional path parameter contains / and retrofit encodes it without that property.
So as i wrote in comments better use two declarations:
#GET("items/list/")
Observable<ResponseItems> getItems();
#GET("items/list/filter/{filter}")
Observable<ResponseItems> getItems(#Path(value = "filter") String filter);
so you may call it like service.getItems("active:true,min_price:100")
In simple word make method over loading. Create two method with same name and different parameter. Check my below source code. (written in kotlin)
#GET("myportal/news/{id}")
fun getNewsList(#Path("id") id: String): Call<NewsEntity>
#GET("myportal/news/{id}/{dateTime}")
fun getNewsList(#Path("id") id: Long, #Path("dateTime") dateTime: String): Call<NewsEntity>
Its better to use HashMap to send optional params in a request, this allows you to send single/multiple optional params in your request also if you send the HashMap empty it will not affect your request.
Your interface will contain code like snippet below
#POST("yourUrl")
#FormUrlEncoded
Call<YourResponse> yourApiRequest(#Header("Authorization") String token,
#Field("mandatoryParam") int mandatoryParam,
#FieldMap Map<String, String> optionalParamMap);
And your API call will be something like the following
private HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("optionalParam1", value1);
hashMap.put("optionalParam2", value2);
ApiClient.getApiClient().getApiInterface().yourApiRequest(token,
mandatoryParam, hashMap)
.enqueue(new Callback<YourResponse>() {
#Override
public void onResponse(Call<YourResponse> call, Response<YourResponse> response) {
}
#Override
public void onFailure(Call<YourResponse> call, Throwable t) {
}
});
In a case where you don't want to send any optional params you can pass an empty hashmap to your request.
Here is code how i have implemented calling.
#Override
public void onClientStarted(SinchClient sinchClient) {
Log.e(TAG, "started");
callClient = sinchClient.getCallClient();
callClient.addCallClientListener(this);
}
public void initiateCall(String receiverId) {
Call call = callClient.callUserVideo(receiverId);
call.addCallListener(this);
}
But i want to pass some data like username, profile image and other things, does there any way to pass those data with video calling?
Got solution from sinch developer.
You can pass along custom data providing headers to the
callUserVideo(String toUserId, Map headers) method. The headers you
pass can be retrieved from the incoming Call object using getHeaders()
method.
https://download.sinch.com/docs/android/latest/reference/com/sinch/android/rtc/calling/Call.html
We can pass extra values in map when initializing call.
HashMap<String,String> map=new HashMap<>();
map.put("userId","5");
map.put("profileImage","image url");
Call call = callClient.callUserVideo(receiverId,map);
I'm writing a library for a REST API. The library will have to hide to the user the internal complexity of the API as much as possible.
I'm relying on retrofit2 configured to use RxJava to wrap the responses and GSon to parse them.
I'm using retrofit for a REST service.
#GET("search")
Observable<SearchResult> search(#Query("q") String query);
#GET("search")
Observable<SearchResult> search(#QueryMap Map<String, String> params);
#GET("search")
Observable<SearchResult> searchNext(#Query("q") String query, #Query("startKey") String startKey);
#GET("search")
Observable<SearchResult> searchNext(#QueryMap Map<String, String> params, #Query("startKey") String startKey);
The search REST service return a JSON
{
"count": 2334,
"nextBatchKey": "SADjdfsahoi023451sadfjlskdfj02134512",
"results": [ .... ]
}
the total count of result
a key to be used to retrieve the next batch of the list
the actual array of results
To obtain the next batch the service has to be called again with the same parameters and the key returned.
For example, suppose doing a search for q=foo and limit=20: the REST API will return the results matching string foo batched in segments for 20 results each. To get the next batch I would need to create another request with the same two parameters, q=foo and limit=20, adding startKey=<the next batch key>.
I want / need to hide from the user of the library these internal mechanism.
Inside my library I initialize retrofit like this:
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient) // added interceptors
.baseUrl(baseUrl) // my base url
.addConverterFactory(GsonConverterFactory.create(gson)) // custom type adapter factory
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
It generate the service for my interface and use Gson to parse the response wrapping it with RxJava Observable.
Then I obtain the service:
MyService service = retrofit.create(MyService.class)
Note that I do not write the code of this service, as retrofit works it generate the service automatically using my interface (MyService) annotations.
From the library user perspective all this is hidden, he will just receive the service and use it like this:
// obtain the service
MyService service = MyLibrarySDK.getService(context);
// perform the search operation
Observable<SearchResult> resultObservable =
service.search(params);
I want the developer using my library to be able to get the next batch like this:
// then later result == the SearchResult
Observable<SearchResult> nextBatch =
result.searchNext();
The searchNext() call takes no parameters. This means the SearchResult object I created in the previous call should internally know which parameters to use when using the method searchNext(Map parameters, String key).
If the user perform the search call with q=foo this information is not received in the response. And I need it.
Since retrofit create the service I don't know how I can intercept the method parameters passed with the call to service.search().
The only idea I had to address this is to use an OkHttp interceptor, place the query parameters map in a ThreadLocal, then use a custom Gson TypeAdapter to inject those parameters into the SearchResult object.
This solution should technically work but I think it's ugly. Furthermore the interceptor would run for any method of the REST API but I need it only for search queries.
Have you got a better / creative / elegant idea?
Don't think you should have have searchNext() on the result but rather on the service.
Try something like below:
MyService.java
public class MyService {
RestClient.GitApiInterface service = RestClient.getClient();
String query;
Observable<GitResult> resultObservable;
public Observable<GitResult> getUsersByName(String query) {
this.query = query;
resultObservable = service.getUsersByName(query, 0).cache();
return resultObservable;
}
public Observable<GitResult> searchNext() {
Observable nextResultObservable = resultObservable
.flatMap(new Func1<GitResult, Observable<?>>() {
#Override
public Observable<?> call(GitResult gitResult) {
return service.getUsersByName(query, gitResult.getNextPage());
}
});
return nextResultObservable;
}
}
Usage
final MyService service = new MyService();
// First load
service.getUsersByName("tom");
// consecutive calls
service.searchNext();
Sample app - https://gitlab.com/wizardkrishna/retrofit2-sample
References
Consuming REST API using Retrofit2
Observable Caching
I am trying to call a RESTful API in my Android app using Volley. The API requires that for each request you pass in a special header (X-AUTH-TOKEN).
I found that I could do this for GET requests by overriding the getHeaders method from the Request class.
However when I try to do this with a POST request, I find that the header is not appended / received by the server.
This is the class I am using to create the request
public class AuthenticatedStringRequest extends StringRequest {
public AuthenticatedStringRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(method, url, listener, errorListener);
}
#Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put("X-AUTH-TOKEN", Constants.AUTH_TOKEN);
return headers;
}
}
Is there a different method of adding special headers with POST requests on Volley?
I'm developing an Android app and I'm kind of new to this. What I'm currently trying to do is to maintain a user logged in. To login I have to make a request to an API and send the user email and password, that returns a JSONObject which I manage just fine. I have a CookieStore variable to get the cookies and store them into my SharedPreferences.
I just realised my cookies get lost if I close the application and I need to keep making requests to the API even if the app gets resumed, because the user has already logged in. I tried to "restore" the cookies in the onResume() method of one of my activities but that doesn't work the way I want it to. If I try to get the cookies using CookieStore.getCookies() after I resume my app that list is null.
I've been told I can use loopj's AsynHttpClient and manage my cookies with PersistentCookieStore but wouldn't that get me to the same problem? I'd be losing the value of the PersistentCookieStore instance every time I resume my app, right?
so my question is:
How can I restore the cookies in order to keep them persistent and make me able to keep making requests to the API?
Hope anyone can help me with this.
Thanks in advance.
You have two options:
Option 1 - If using SharedPreferences and HttpUrlConnection: you need to manually retrieve the cookie from the SharedPreferences and add the cookie to each request when using HttpUrlConnection.
Option 2 - If using loopj AsyncHttpClient: According to the loopj documentation, you must create an instance of PersistentCookieStore and add it to your AsyncHttpClient every time your app is restarted, like so
AsyncHttpClient myClient = new AsyncHttpClient();
PersistentCookieStore myCookieStore = new PersistentCookieStore(this);
myClient.setCookieStore(myCookieStore);
where 'this' is a Context.
How do you store/restore your cookies to/from the SharedPreferences? Can you share some code?
Also can you try debugging it and tell us what's wrong with it? Does it not get stored to the SharedPreferences? Does it fail to restore them?
Before storing/restoring the CookieStore to the SharedPreferences, you would need to create a Serializable object with it first, such as an ArrayList<HashMap<String, String>
Something like:
public static void storeCookies(Context context, CookieStore cookieStore) {
ArrayList<HashMap<String, String> cookies = new ArrayList<HashMap<String, String>();
for (Cookie cookie : cookieStore.getCookies()) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", cookie.getName());
map.put("path", cookie.getPath());
map.put("domain", cookie.getDomain());
map.put("expiry_date", cookie.getExpiryDate());
map.put("ports", cookie.getPorts());
map.put("value", cookie.getValue());
map.put("version", cookie.getVersion());
...
// Add all the fields you want to save
cookies.add(map);
}
SharedPreferences.Editor editor = context.getSharedPreferences().edit();
editor.putSerializable("cookies", cookies).apply();
}
public static void addCookiesToStore(Context context, CookieStore cookieStore) {
List<Map<String, String>> cookies = (ArrayList<HashMap<String, String>>) context.getSharedPreferences().getSerializable("cookies");
for (Cookie map : cookieStore.getCookies()) {
Cookie cookie = new BasicClientCookie(map.getName(), map.getValue());
cookie.setPath(map.get("path));
...
// Add all the fields you want to restore
cookieStore.add(cookie);
}
}