GCM push message encoding - android

I am trying to send push notifications using following code:
Message message = new Message.Builder().addData("appName", appData.name)
.addData("message", pushData.message).build();
On the receiving side my code is:
String message = intent.getStringExtra("message");
When the message is in English, latin charset, everything works. However, when I try other languages or the character ç, they arrive as question marks or are deleted from the string.
Note: it's encoded in utf-8

Java server
Message messagePush = new Message.Builder().addData("message", URLEncoder.encode("your message éèçà", "UTF-8")))
Android application
String message = URLDecoder.decode(intent.getStringExtra("message"), "UTF-8");

I was having the same issue. Non ASCII characters where corrupted when received on the Android client. I personally believe this is a problem within the Google GCM server library implementation.
In the Android GCM library I see the method:
com.google.android.gcm.server.Sender.sendNoRetry(Message, List<String>)
That method does the following
HttpURLConnection conn = post(GCM_SEND_ENDPOINT, "application/json", requestBody)
They should atleast specify "application/json; charset=utf-8" or whatever encoding they used or better yet force it to UTF-8. Isn't this is a BIG problem?
Digging even deeper I find the method:
com.google.android.gcm.server.Sender.post(String, String, String)
which does:
byte[] bytes = body.getBytes()
Why do they use the platform default charset? Especially since it is not likely to align with the devices default charset.
Work Around
Pass the following property as an argument to the JVM "-Dfile.encoding=UTF-8" This instructs Java to use UTF-8 as the platform default charset when doing things like "blah".getBytes(). This is bad practice but what can you do when it is someone else's library?

I had a similar problem with the gcm-server library. My workaround was using a custom sender to override the post method and use UTF8 as encoding in the getBytes() call.
It works on Google App Engine.
The code of the custom sender class:
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.logging.Level;
import com.google.android.gcm.server.Sender;
/**
* Workaround to avoid issue #13 of gcm-server
* #see https://code.google.com/p/gcm/issues/detail?id=13&q=encoding
*
* #author rbarriuso /at/ tribalyte.com
*
*/
public class Utf8Sender extends Sender {
private final String key;
public Utf8Sender(String key) {
super(key);
this.key = key;
}
#Override
protected HttpURLConnection post(String url, String contentType, String body) throws IOException {
if (url == null || body == null) {
throw new IllegalArgumentException("arguments cannot be null");
}
if (!url.startsWith("https://")) {
logger.warning("URL does not use https: " + url);
}
logger.fine("Utf8Sender Sending POST to " + url);
logger.finest("POST body: " + body);
byte[] bytes = body.getBytes(UTF8);
HttpURLConnection conn = getConnection(url);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(bytes.length);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", contentType);
conn.setRequestProperty("Authorization", "key=" + key);
OutputStream out = conn.getOutputStream();
try {
out.write(bytes);
} finally {
close(out);
}
return conn;
}
private static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
// ignore error
logger.log(Level.FINEST, "IOException closing stream", e);
}
}
}
}

These were some good solutions, however they didnt help me because I was using topic messaging to send notifications. As per this
The HTTP header must contain the following headers:
Authorization: key=YOUR_API_KEY
Content-Type: application/json for
JSON; application/x-www-form-urlencoded;charset=UTF-8 for plain text.
If Content-Type is omitted, the format is assumed to be plain text.
However, because I am using cloud endpoints and my app (already out in the wild) is expecting json, formatting the request in plain text wasnt viable. The solution was to ignore the document above, and in my backend, format the http request header as such:
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
Like magic, suddenly all special characters (read: Japanese) is going through to my application with no front end changes being made. My complete http post code is below( where payload = Cloud endpoint model object, converted to json<String> via Gson):
try {
URL url = new URL("https://gcm-http.googleapis.com/gcm/send");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
connection.setRequestProperty("Authorization", "key=" + API_KEY);
connection.setRequestMethod("POST");
byte[] bytes=payload.getBytes("UTF-8");
OutputStream out = connection.getOutputStream();
try {
out.write(bytes);
} finally {
out.close();
}
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// OK
log.warning("OK");
} else {
// Server returned HTTP error code.
log.warning("some error "+connection.getResponseCode());
}
} catch (MalformedURLException e) {
// ...
}

Break with the debugger and view the bytes of the message you think you're sending

Related

JsonWriter POST not working in Android to WCF web service

I would like to know anyone has a sample code on how to use JsonWriter to post JSON data to WCF web service from Android?
I tested my WCF with Fiddler 4 (Composer with POST json data) and it gave me the correct return.
However, when I tested with my Android application which use JsonWriter, I didn't see any action on Fiddler (I set up Fiddler to check on my Android Emulator network traffic, by the way, I am testing on Android Emulator.).
With the same Android application, I can call GET with JsonReader to my WCF and get the correct reply.
Its just calling POST with JsonWriter got no response code or no action in Fiddler.
For JsonWriter (and Reader), I refer to Android developer >> JsonWriter
Here are my test results (Get and Post) with Emulator GET and POST.
Here are my test results with Fiddler direct POST.
First it gave me Result 307 then follow by 200.
And here is how I use JsonWriter to post (this block was from AsyncTask).
try
{
Log.d("TEST_JSON", "URL: " + params[0]);
URL url = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept","application/json");
conn.setRequestProperty("Content-Type","application/json");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setAllowUserInteraction(false);
// conn.connect();
OutputStream out = conn.getOutputStream();
JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
try
{
writer.setIndent(" ");
if(params[1].trim() == "ARRAY")
{
// Write array to WCF.
}
else if(params[1].trim() == "OBJ")
{
// Write object to WCF. <<== I am testing with one object.
writer.beginObject();
writer.name("ShipNo").value("SI10101");
writer.name("DoNo").value("DO230401");
writer.name("PartNo").value("102931-1201");
writer.name("Qty").value(1);
writer.name("ShipIn").value(1);
writer.endObject();
}
}
finally
{
writer.close();
out.close();
}
// If I enable below blocks, I will see 307 response code in Fiddler.
/*
conn.connect();
int responseCode = conn.getResponseCode();
Log.d("TEST_JSON", "Code: " + String.valueOf(responseCode));
*/
Log.d("TEST_JSON", "Finish sending JSON.");
conn.disconnect();
}
catch (IOException e)
{
Log.e("TEST_JSON",e.getMessage()); // <<-- No error from this try catch block.
}
I tried and still cannot figure out why JsonWriter didn't trigger to my WCF (I attached my WCF to my localhost service, only Fiddler direct POST will hit the break point in my WCF project while Android App didn't reach to it). I follow the exact example from Android Developer site though. I google and didn't find any site on using JsonWriter with OutputStreamWriter (I saw some post using StringWriter).
May I know where did my code wrong ?
Based on this StackOverFlow post WCF has a 'Thing' about URI, I managed to solve this issue.
All I need is to make sure my POST web service has URI Template ends with "Slash".
Example: http://10.72.137.98/myWebSvc/posvctFun/
Instead of http://10.72.137.98/myWebSvc/postFun

httpurlconnection and special characters

i can send with my app a http post.
Problem is, that the special characters like ä, ö , ... will not be correct.
this is my code:
#Override
protected String doInBackground(String... params) {
try {
URL url = new URL("https://xxx");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
String urlParameters = "&name" + name;
connection.setRequestMethod("POST");
connection.setRequestProperty("Accept-Charset", "UTF-8");
connection.setDoOutput(true);
DataOutputStream dStream = new DataOutputStream(connection.getOutputStream());
dStream.writeBytes(urlParameters);
dStream.flush();
dStream.close();
} catch (MalformedURLException e) {
Log.e("-->", Log.getStackTraceString(e));
} catch (IOException e) {
Log.e("-->", Log.getStackTraceString(e));
}
return null;
}
This http post will send to a php file, which saves the value name into a database.
Example:
The App send the value "Getränke"
Result in the database: "Getr"
where is my mistake?
Try this it may be help to you.
You need to set the encoding in your Content-Type header.
Set it to application/x-www-form-urlencoded; charset=utf-8. instead of
Accept-Charset.
The internal string representation in Java is always UTF-16.
Every encoding has to be done when strings enter or leave the VM. That means in your case you have to set the encoding when you write the body to the stream.
Try with following:
dStream.writeBytes(urlParameters.getBytes("UTF-8"));
Also you may need to set the encoding in your Content-Type header.
Set it to "application/x-www-form-urlencoded; charset=utf-8".
Currently you are only setting the Accept-Charset - this tells the server what to send back.

Prestashop and web service(Restful), HttpURLConnection, BufferedReader

I want to access the resources of a site created by prestashop via restful web services, which I enter the URL you must enter a key (that is generated by prestashop when we create a restful web service) in the field of username.
so I am trying to read a xml string:
<?xml version="1.0" encoding="UTF-8"?>
<prestashop>
<manufacturers>
<manufacturer id="1" xlink:href="http://127.0.0.1/test/api/manufacturers/1" />
<manufacturer id="2" xlink:href="http://127.0.0.1/test/api/manufacturers/2" />
</manufacturers>
</prestashop>
over HTTP:
I have the following code:
public class MainTest
{
public static String readUrl(HttpURLConnection conn) throws Exception
{
BufferedReader reader = null;
try
{
reader = new BufferedReader(new InputStreamReader((conn.getInputStream())));
StringBuffer buffer = new StringBuffer();
int read;
char[] chars = new char[1024];
while ((read = reader.read(chars)) != -1)
buffer.append(chars, 0, read);
return buffer.toString();
} finally
{
if (reader != null)
reader.close();
}
}
public static void main(String[] args) throws Exception
{
URL url = new URL("http://127.0.0.1/test/api/manufacturers");
HttpURLConnection conn = null;
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(30000);
conn.setRequestProperty("Accept", "application/XML");
conn.setRequestProperty("Authentication-Key", "ALHKUNM0J6JJAQC21E4TVWHBM6FAKACF");
System.out.println("true2");
String xml="";
xml = readUrl(conn);
System.out.println(xml);
}
}
but it give me this error
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 401 for URL: http://127.0.0.1/test/api/manufacturers
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at com.test.services.URLReader.main(URLReader.java:28)
i think the problem is in this ligne
reader = new BufferedReader(new InputStreamReader((conn.getInputStream())));
please help me if you have any solution
regards
You request property for authentication is wrong!
First, Prestashop REST API is using basic authentication.
Then you will need to encrypt your credentials based on base64 encryption.
So download commons-codec-1.5.jar from http://commons.apache.org/proper/commons-codec/.
Here is the way I did it.
import org.apache.commons.codec.binary.Base64
//.....
String username = "Your Prestashop webservice key";
String password = "";// leave it empty
String authToBytes = username + ":" + password;
//....
byte authBytes = Base64.encodeBase64(authToBytes.getBytes())// I keep it generic
String authBytesString = new String(authBytes);
//then your code
conn.setRequestProperty("Authorization", "Basic " + authBytesString);
//...
It should work now.
Find a small Prestashop java API at http://www.onasus.com/2012/10/3712/prestashop-web-services-java-api/
I found many many other ways to consume the web services. One of them uses the class java.net.Authenticator which handles the HTTP Basic authentication for you automatically. Find out more at http://examples.javacodegeeks.com/core-java/net/authenticator/access-password-protected-url-with-authenticator/ .
From HTTP Status Codes:
401 Unauthorized
Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided.[2] The response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource. See Basic access authentication and Digest access authentication.
Try to find out what kind of authentication your service demands. Often it's easier to play around with curl before you write a program with HttpURLConnection.
curl -v \
-G \
-H 'Accept: application/xml' \
-H 'WWW-Authenticate: Basic 938akjsdfh==' \
http://127.0.0.1/test/api/manufacturers
Another thing: you should always check the response code before accessing getInputStream(). If you encounter something like 4xx, getInputStream() will throw an exception. In this case, maybe you can grab some information of the error if you read from getErrorStream().

Sending HTTP DELETE request in Android

My client's API specifies that to remove an object, a DELETE request must be sent, containing Json header data describing the content. Effectively it's the same call as adding an object, which is done via POST. This works fine, the guts of my code is below:
HttpURLConnection con = (HttpURLConnection)myurl.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setUseCaches(false);
con.connect();
OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream());
wr.write(data); // data is the post data to send
wr.flush();
To send the delete request, I changed the request method to "DELETE" accordingly. However I get the following error:
java.net.ProtocolException: DELETE does not support writing
So, my question is, how do I send a DELETE request containing header data from Android? Am I missing the point - are you able to add header data to a DELETE request? Thanks.
The problematic line is con.setDoOutput(true);. Removing that will fix the error.
You can add request headers to a DELETE, using addRequestProperty or setRequestProperty, but you cannot add a request body.
This is a limitation of HttpURLConnection, on old Android versions (<=4.4).
While you could alternatively use HttpClient, I don't recommend it as it's an old library with several issues that was removed from Android 6.
I would recommend using a new recent library like OkHttp:
OkHttpClient client = new OkHttpClient();
Request.Builder builder = new Request.Builder()
.url(getYourURL())
.delete(RequestBody.create(
MediaType.parse("application/json; charset=utf-8"), getYourJSONBody()));
Request request = builder.build();
try {
Response response = client.newCall(request).execute();
String string = response.body().string();
// TODO use your response
} catch (IOException e) {
e.printStackTrace();
}
getOutputStream() only works on requests that have a body, like POST. Using it on requests that don't have a body, like DELETE, will throw a ProtocolException. Instead, you should add your headers with addHeader() instead of calling getOutputStream().
I know is a bit late, but if anyone falls here searching on google like me I solved this way:
conn.setRequestProperty("X-HTTP-Method-Override", "DELETE");
conn.setRequestMethod("POST");
DELETE request is an extended form of GET request, as per the android documentation you cannot write in the body of DELETE request.
HttpUrlConnection will throw "unable to write protocol exception".
If you still want to write the parameter in the body, i suggest you to use the OKHttp Library.
OKHttp documentation
If you are intrested to use more simpler library then you can try
SimpleHttpAndroid library
One thing to remember here is if you are not writing anything in the body then remove the line
conn.setDoOutput(true);
Thanks, Hopefully it may help.
Try below method for call HttpDelete method, it works for me, hoping that work for you as well
String callHttpDelete(String url){
try {
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 15000);
HttpConnectionParams.setSoTimeout(httpParams, 15000);
//HttpClient httpClient = getNewHttpClient();
HttpClient httpClient = new DefaultHttpClient();// httpParams);
HttpResponse response = null;
HttpDelete httpDelete = new HttpDelete(url);
response = httpClient.execute(httpDelete);
String sResponse;
StringBuilder s = new StringBuilder();
while ((sResponse = reader.readLine()) != null) {
s = s.append(sResponse);
}
Log.v(tag, "Yo! Response recvd ["+s.toString()+"]");
return s.toString();
} catch (Exception e){
e.printStackTrace();
}
return s.toString();
}
You can't just use the addHeader() method?
Here is my Delete request method.
Simply it is post request with extra RequestProperty
connection.setRequestProperty("X-HTTP-Method-Override", "DELETE");
Below the complete method.
public void executeDeleteRequest(String stringUrl, JSONObject jsonObject, String reqContentType, String resContentType, int timeout) throws Exception {
URL url = new URL(stringUrl);
HttpURLConnection connection = null;
String urlParameters = jsonObject.toString();
try {
connection = (HttpURLConnection) url.openConnection();
//Setting the request properties and header
connection.setRequestProperty("X-HTTP-Method-Override", "DELETE");
connection.setRequestMethod("POST");
connection.setRequestProperty("User-Agent", USER_AGENT);
connection.setRequestProperty(CONTENT_TYPE_KEY, reqContentType);
connection.setRequestProperty(ACCEPT_KEY, resContentType);
connection.setReadTimeout(timeout);
connection.setConnectTimeout(defaultTimeOut);
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
// Send request
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
responseCode = connection.getResponseCode();
// To handle web services which server responds with response code
// only
try {
response = convertStreamToString(connection.getInputStream());
} catch (Exception e) {
Log.e(Log.TAG_REST_CLIENT, "Cannot convert the input stream to string for the url= " + stringUrl + ", Code response=" + responseCode + "for the JsonObject: " + jsonObject.toString(), context);
}
} catch (
Exception e
)
{
if (!BController.isInternetAvailable(context)) {
IntentSender.getInstance().sendIntent(context, Constants.BC_NO_INTERNET_CONNECTION);
Log.e(Log.TAG_REST_CLIENT, "No internet connection", context);
}
Log.e(Log.TAG_REST_CLIENT, "Cannot perform the POST request successfully for the following URL: " + stringUrl + ", Code response=" + responseCode + "for the JsonObject: " + jsonObject.toString(), context);
throw e;
} finally{
if (connection != null) {
connection.disconnect();
}
}
}
I hope it helped.
To add closure to this question, it transpired that there is no supported method to send an HTTP DELETE request containing header data.
The solution was for the client to alter their API to accept a standard GET request which indicated that the action should be a delete, containing the id of the item to be deleted.
http://clienturl.net/api/delete/id12345

Does Android Support a server made on ashx or ASP.NET Web Handler file

I have an android application that have to communicate with the server using post method in HTTP. My application work fine whenever i open some other sites having basic page may be html or others but whenever i want to open my server file it gives nothing even though with get method in HTTP.
The blank response from the server can be understandable that what i am getting because i have to send some headers with post method as a request and ashx will send some response to it.
But still as expected by get method in HTTP the basic information of the page have to be retrieved.
for eg. my server url is http://172.17.3.90/RMALite/RLHandler.ashx
and the basic response from the get method have to be like this.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; charset=windows-1252"></HEAD>
<BODY></BODY></HTML>
Unfortunately i am getting nothing
but whenever i changed the url to open other sites it work fine and i got the response in text html format.
So my Question is, Android require some stuff to handle ASP.NET handler or ashx file as compare to other sites or URL's?
I know it late now but other can get benefit. Below is code snippet which help me to get accomplished same issue.
public static String excutePost(String targetURL, String urlParameters) {
//targetURL =http://172.17.3.90/RMALite/RLHandler.ashx
URL url;
HttpURLConnection connection = null;
try {
// Create connection
url = new URL(targetURL);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", ""
+ Integer.toString(urlParameters.getBytes().length));
connection.setRequestProperty("Content-Language", "en-US");
connection.setRequestProperty("method-name", "parameter-value");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
// // Send request
DataOutputStream wr = new DataOutputStream(connection
.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
String field1 = connection.getHeaderField("field1");
String field2 = connection.getHeaderField("field2");
return anyArray;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
I believe it would be helpful n much appreciated to this post
thanks much
-y

Categories

Resources