while making connection using HttpClient in android from HttpResponse able to get all possible "set-cookie" header value (JESSIONID and XSCRF-TOKEN).Check below screenshot.
Now working with android studio with volley api for connection , i am getting only single value of "set-cookie" header (JESSIONID only).See below :
I have also check https://groups.google.com/forum/#!topic/volley-users/rNTlV-LORzY.
For which have to make change in volley api jar project. But don't know how to edit volley api. If any other solution present kindly guide.
Kindly help to retrieve multiple value of "set-cookie" using volley api.
Problem:
The problem is inside the Volley unfortunately. I had this problem and after many searches i figured out that there is a method called convertHeaders in BasicNetwork class that handles headers like this:
protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}
You see the result is Map<String, String> which can't contain same keys with different values. so you always have only last cookie.
The standard of cookie setting tells us we should separate cookies with ; for example if you want to contain 2 key-value in a request cookie you should put them like this:
Cookies: k1=v1;k2=v2
Solution:
In your case you have two options.
1 - change your code in Server-Side so that the response contains only 1 Set-Cookie separated key-values by ;. example of your response:
Set-Cookie: JESSIONID=qZtQ...;Path=/;HttpOnly;XSRF-TOKEN=6c65...
2 - get Volley source code and change that buggy method and make a fixed .jar again! this option is my favorite cause you didn't touch the response of server
My implementation of this method is:
protected static Map<String, String> convertHeaders(Header[] headers) {
TreeMap result = new TreeMap(String.CASE_INSENSITIVE_ORDER);
for(int i = 0; i < headers.length; ++i) {
String headerName = headers[i].getName();
if(!result.containsKey(headerName)) {
result.put(headers[i].getName(), headers[i].getValue());
} else {
String value = (String)result.get(headerName);
String mergedValue = value + ";" + headers[i].getValue();
result.remove(headerName);
result.put(headerName, mergedValue);
}
}
return result;
}
There is workaround for this in:
implementation "com.android.volley:volley:1.1.0"
"NetworkResponse (and Cache.Entry) now includes an "allHeaders" field which is the raw list of all headers returned by the server and thus can include duplicates by name."
Source: https://github.com/google/volley/issues/21
Example:
private static final String COOKIE_KEY = "Set-Cookie";
private static final String COOKIE_NAME = "NameOfOneOfTheCookies";
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
handleCookies(response);
String parsed;
try {
parsed = new String(response.data, "utf-8");
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed,
HttpHeaderParser.parseCacheHeaders(response));
}
private void handleCookies(NetworkResponse response) {
for (Header header : response.allHeaders) {
if (header.getName().equals(COOKIE_KEY) && header.getValue().startsWith(COOKIE_NAME)) {
getCookies(response);
}
}
}
private void getCookies(NetworkResponse response) {
ArrayList<String> cookiesList = new ArrayList<>();
for (Header header : response.allHeaders) {
if (header.getName().equals(COOKIE_KEY)) {
cookiesList.add(header.getValue());
}
}
// TODO Do something with the cookiesList
}
Related
Aim
In a fragment, I have a search bar which looks for online news about what the user typed. I would want to display these news (title + description + date of publication + ... etc.) in the GUI, as vertical blocks.
Implementation
Explanations
In the fragment, within the search event handling, I instanciated an asynchronous task and execute it with the good URL REST API I use to do the search.
In the asynchronous task, I make use of this REST API (thanks to the URL and some required parameters as an authorization key, etc.). When my asynchronous task gets answered, it must update the fragment's GUI (i.e.: it must vertically stack GUI blocks containing the titles, descriptions, etc. of the got news).
Sources
You will find sources in the last part of this question.
My question
In the asynchronous task (more precisely: in its function that is executed after having got the answer), I don't know how to get the calling fragment. How to do this?
Sources
Fragment part
private void getAndDisplayNewsForThisKeywords(CharSequence keywords) {
keywords = Normalizer.normalize(keywords, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");
new NetworkUseWorldNews().execute("https://api.currentsapi.services/v1/search?keyword=" + keywords + "&language=en&country=US");
}
Asynchronous task part
public class NetworkUseWorldNews extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String[] urls) {
StringBuilder string_builder = new StringBuilder();
try {
URL url = new URL(urls[0]);
HttpsURLConnection https_url_connection = (HttpsURLConnection) url.openConnection();
https_url
_connection.setRequestMethod("GET");
https_url_connection.setDoOutput(false);
https_url_connection.setUseCaches(false);
https_url_connection.addRequestProperty("Authorization", "XXX");
InputStream input_stream = https_url_connection.getInputStream();
BufferedReader buffered_reader = new BufferedReader(new InputStreamReader(input_stream));
String line;
while((line = buffered_reader.readLine()) != null) {
string_builder.append(line);
}
buffered_reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return string_builder.toString();
}
#Override
protected void onPostExecute(String result) {
try {
JSONObject news_response_http_call = new JSONObject(result);
switch(news_response_http_call.getString("status")) {
case "ok":
JSONArray news = news_response_http_call.getJSONArray("news");
for(int i = 0; i < news.length(); i++) {
JSONObject a_news = news.getJSONObject(i);
String title = a_news.getString("title");
String description = a_news.getString("description");
String date_of_publication = a_news.getString("published");
String url = a_news.getString("url");
String image = a_news.getString("image");
System.out.println(title + ": " + date_of_publication + "\n" + image + "\n" + url + "\n" + description);
WorldNewsFragment world_news_fragment = ...;
}
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
If I am right, you want to update View of your caller Fragment. if FragmentA called service then FragmentA should be update.
However the approach you are asking is wrong. Instead of getting caller Fragment in your AsyncTask response. You should do it with Callback.
So now you will need to pass callback in AsyncTask. So instead of posting full code, here are already answers with this problem.
Finally your calling syntax will look like.
NetworkUseWorldNews task = new NetworkUseWorldNews(new OnResponseListener() {
#Override
public void onResponse(String result) {
// Either get raw response, or get response model
}
});
task.execute();
Actually I am still very unclear about your question. Let me know in comments if you have more queries.
Must checkout
Retrofit or Volley for calling Rest APIs
Gson for parsing JSON response automatically to models
I need a working example for a custom API for Microsoft Azure App Service.
I could not get any useful or working information/examples for that, or they just show each time different approaches which are outdated?!?!
For now I have a working table controller which gets information from database and returns it back to my Android client. Now I need to define a custom API Controller to get a string back. In the examples they are all sending an object to the service in order to get an object back. I do not want to send anything to the API, just retrieve some information back from a GET Request.
Regards
// EDIT - Added / edited client / server code to Post a String.
You can use the following code to do a GET request on the auto generated API controller Visual Studio creates (ValuesController).
private void getStringFromAzure() throws MalformedURLException {
// Create the MobileService Client object and set your backend URL
String yourURL = "https://yourApp.azurewebsites.net/";
MobileServiceClient mClient = new MobileServiceClient(yourURL, this);
// Your query pointing to yourURL/api/values
ListenableFuture<JsonElement> query = mClient.invokeApi("values", null, GetMethod, null);
// Callback method
Futures.addCallback(query, new FutureCallback<JsonElement>() {
#Override
public void onSuccess(JsonElement jsonElement) {
// You are expecting a String you can just output the result.
final String result = jsonElement.toString();
// Since you are on a async task, you need to show the result on the UI thread
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onFailure(Throwable throwable) {
Log.d(TAG, "onFailure: " + throwable.getMessage());
}
});
}
public void sendString(final String someString) throws MalformedURLException {
// Your query pointing to /api/values/{String}
ListenableFuture<JsonElement> query = mClient.invokeApi("values/" + someString, null, PostMethod, null);
// Callback method
Futures.addCallback(query, new FutureCallback<JsonElement>() {
#Override
public void onSuccess(JsonElement jsonElement) {
// You are expecting a String you can just output the result.
final String result = jsonElement.toString();
}
#Override
public void onFailure(Throwable throwable) { }
});
}
The backend API: (ValuesController)
{
// Use the MobileAppController attribute for each ApiController you want to use
// from your mobile clients
[MobileAppController]
public class ValuesController : ApiController
{
// GET api/values
public string Get()
{
return "Hello World!";
}
// POST api/values/inputString
public string Post(string inputString)
{
return inputString;
}
}
}
You can also send parameters along in the following way:
List<Pair<String, String>> parameters = new ArrayList<>();
parameters.add(new Pair<>("name", "John"));
parameters.add(new Pair<>("password", "fourwordsalluppercase"));
ListenableFuture<JsonElement> query = client.invokeApi("yourAPI", PostMethod, parameters);
Or as json in the body:
JsonObject body = new JsonObject();
body.addProperty("currentPassword", currentPassword);
body.addProperty("password", password);
body.addProperty("confirmPassword", confirmPassword);
ListenableFuture<JsonElement> query = mClient.invokeApi("yourAPI", body, PostMethod, null);
Based on my understanding, I think there are two parts in your question which include as below. And I think you can separately refer to two sections to get the answers and write your own example.
How to define a custom API on Azure Mobile App to retrieve data from database? Please refer to the section Custom APIs to know how to do with Azure Mobile App backend.
How to call a custom API from Android App? Please refer to the section How to: Call a custom API to know how to do with Android SDK.
I am trying to encode a string which contains a URL, I have a strange issue where the complete string is not being returned, I have noticed that it may be related to the underscore, I have tried a few solution where I replace the underscore, but haven't had much luck with that solution. Below is the JSON.
[{"id":"1","source":"BBC WORLD NEWS",
"time_date":"Sat, 25 Oct 2014 10:49:13",
"title":"Iran hangs woman despite campaign","description":"Iran defies an international campaign and hangs a woman who killed a man she said was trying to sexually abuse her.",
"link":"http:\/\/www.bbc.co.uk\/news\/world-middle-east-29769468#sa-ns_mchannel=rss&ns_source=PublicRSS20-sa",
"image":"http:\/\/news.bbcimg.co.uk\/media\/images\/78529000\/jpg\/_78529517_78528720.jpg"},
I am trying to retrieve the image element from the json. The following is what I receive from my parsing.
http://news.bbcimg.co.uk/media/images/78526000/jpg
I am using this code at the moment:
String imageurl = feed.getImage();
try {
imageurl = URLDecoder.decode(imageurl, "UTF-8");
System.out.println("---------------------------"+imageurl);
imageurl.replace("_", "%5f");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
getimage method:
private String image;
public String getImage() {
return image;
}
private void requestNewsData(String uri) {
RestAdapter api = new RestAdapter.Builder().setEndpoint(ENDPOINT).build();
NewsAPI restapi = api.create(NewsAPI.class);
restapi.news(new Callback<List<RssObject>>() {
public void success(final List<RssObject> newsFeed, Response response) {
Log.v("nas", "the webservice success " + response.getReason());
for (int i = 0; i < newsFeed.size(); i++) {
System.out.println(newsFeed.get(i).description);
newsList.add(newsFeed.get(i).description);
FeederModel feed = new FeederModel();
feed.setSource(newsFeed.get(i).source);
feed.setImage(newsFeed.get(i).image); // adding setimage
}
}
The retrieved string is missing the final part of the url.
Any suggestion would be gratefully appreciated. Thanks.
Trying to use Volley lib as a network wrapper for my android application. I have a connection up and running, but the problem is that every time there is multiple "Set-Cookie" headers in the response Volley uses Map that cannot have duplicate keys, and will only store the last Set-cookie header and overwrite the rest.
Is there a workaround for this issue?
Is there another lib to use?
I tried overiding classes to fix this but when I had to edit NetworkResponse, I was descending too far down the rabbithole. So I decided to just edit Volley directly to grab all response headers in an array and not a Map.
My fork is on GitHub and I included an example usage activity.
I made changes to NetworkResponse.java, BasicNetwork.java and HurlStack.java as detailed in this commit.
Then to use in your actual apps you do something like this
protected Response<String> parseNetworkResponse(NetworkResponse response) {
// we must override this to get headers. and with the fix, we should get all headers including duplicate names
// in an array of apache headers called apacheHeaders. everything else about volley is the same
for (int i = 0; i < response.apacheHeaders.length; i++) {
String key = response.apacheHeaders[i].getName();
String value = response.apacheHeaders[i].getValue();
Log.d("VOLLEY_HEADERFIX",key + " - " +value);
}
return super.parseNetworkResponse(response);
}
It's a dirty little hack but seems to work well for me at the moment.
The first thing you need is to modify BasicNetwork.convertHeaders method to make it support multiple map values. Here is example of modified method:
protected static Map<String, List<String>> convertHeaders(Header[] headers) {
Map<String, List<String>> result = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < headers.length; i++) {
Header header = headers[i];
List<String> list = result.get(header.getName());
if (list == null) {
list = new ArrayList<String>(1);
list.add(header.getValue());
result.put(header.getName(), list);
}
else list.add(header.getValue());
}
return result;
}
Next thing you need is to modify DiskBasedCache.writeStringStringMap and DiskBasedCache.readStringStringMap methods. They should support multiple values. Here are modified methods along with helper methods:
static void writeStringStringMap(Map<String, List<String>> map, OutputStream os) throws IOException {
if (map != null) {
writeInt(os, map.size());
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
writeString(os, entry.getKey());
writeString(os, joinStringsList(entry.getValue()));
}
} else {
writeInt(os, 0);
}
}
static Map<String, List<String>> readStringStringMap(InputStream is) throws IOException {
int size = readInt(is);
Map<String, List<String>> result = (size == 0)
? Collections.<String, List<String>>emptyMap()
: new HashMap<String, List<String>>(size);
for (int i = 0; i < size; i++) {
String key = readString(is).intern();
String value = readString(is).intern();
result.put(key, parseNullStringsList(value));
}
return result;
}
static List<String> parseNullStringsList(String str) {
String[] strs = str.split("\0");
return Arrays.asList(strs);
}
static String joinStringsList(List<String> list) {
StringBuilder ret = new StringBuilder();
boolean first = true;
for (String str : list) {
if (first) first = false;
else ret.append("\0");
ret.append(str);
}
return ret.toString();
}
And last thing is HttpHeaderParser class. You should make its parseCacheHeaders method support multiple values. Use the following helper method for this:
public static String getHeaderValue(List<String> list) {
if ((list == null) || list.isEmpty()) return null;
return list.get(0);
}
And the latest thing to modify is a bunch of places to replace
Map<String, String>
to
Map<String, List<String>>
Use your IDE to do this.
Question pretty old, but if helps someone. In newest volley you have:
protected Response<String> parseNetworkResponse(NetworkResponse response)
{
List<Header> headers = response.allHeaders;
String sessionId = null;
for (Header header : headers)
{
// header.getName();
// header.getValue();
}
return super.parseNetworkResponse(response);
}
You can override Network class of volley. Looking at performRequest and convertHeaders methods of BasicNetwork might help. Then, passing your Network implementation to the contructor of RequestQueue like:
new RequestQueue(new NoCache(), new YourOwnNetwork());
I am creating an android app for my facebook page. The app is supposed to display random statuses(not just the recent ones) from the facebook page. Is there anyway I could do this?
I haven't done anything of that kind ever, but I think you can gran some logic from this and get it to work.
Step 1:
Make a call to the Facebook API, get all Status Updates and in a for loop, add them to an ArrayList<String>. For example, Facebook returns its data in JSON format. I am assuming, you already know how to fetch data. You need to parse the "message" tag from the JSON data returned by your Facebook API call.
For example:
ArrayList<String> arrStatusMessage;
for (int i = 0; i < JAFeeds.length(); i++) {
JSONObject JOFeeds = JAFeeds.getJSONObject(i);
if (JOFeeds.has("message")) {
String strStatusMessage = JOFeeds.getString("message");
arrStatusMessage.add(strStatusMessage );
}
}
Step 2:
Once you have your entire set of Facebook Status Messages, you will now need to use a java.util.Random instance.
For example: (Please note: I have not tested this code and it might result in errors. You may have to play around with it a bit to get it to work. :-( )
private static final Random randomGenerator = new Random();
int intRandom = randomGenerator.nextInt(arrStatusMessage.size());
String strRandomStatus = arrStatusMessage.get(intRandom);
Step 3:
Use the strRandomStatus to set it on a TextView.
For example:
TextView txtRanStatus = (TextView) findViewById(R.id.txtRanStatus);
txtRanStatus.setText(strRandomStatus);
You haven't posted any code, so it is difficult to provide something that fits in your scheme of things. But I think this should get you started. You will, possibly, need to adapt a few things and fit them in your own code.
Hope this helps.
EDIT: As per a comment by th OP, adding some bits of code to fetch Facebook Status Messages:
in your onCreate() method:
Start a new AsyncTask:
new getFacebookFeeds().execute();
I use this method in my app to make the Facebook Call to get all feeds from the Graph API.
private class getFacebookFeeds extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
String URL = "https://graph.facebook.com/me/home&access_token=ACCESS_TOKEN?limit=10";
try {
HttpClient hc = new DefaultHttpClient();
HttpGet get = new HttpGet(URL);
HttpResponse rp = hc.execute(get);
if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String result = EntityUtils.toString(rp.getEntity());
// GET THE INTIAL RESULTS JSON ROOT
JSONObject JORoot = new JSONObject(result);
// GET THE "DATA" TAG FOR FEEDS ROOT
JSONArray JAFeeds = JORoot.getJSONArray("data");
for (int i = 0; i < JAFeeds.length(); i++) {
JSONObject JOFeeds = JAFeeds.getJSONObject(i);
if (JOFeeds.has("message")) {
String strStatusMessage = JOFeeds.getString("message");
arrStatusMessage.add(strStatusMessage );
}
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
You can do the remaining code, where you select a random Status Update, in the onPostExecute() of the AsyncTask shown above:
#Override
protected void onPostExecute(Void result) {
int intRandom = randomGenerator.nextInt(arrStatusMessage.size());
String strRandomStatus = arrStatusMessage.get(intRandom);
txtRanStatus.setText(strRandomStatus);
}
Declare the TextView as a Global Variable and cast it on your onCreate() before calling the AsyncTask. I think this should work just fine. Let me know how it goes. :-)