Java HttpUrlConnection POST request special characters strange behavior - java

i'm trying to implements a POST request with HttpURLConnection. This is my code:
private static void call(String body) throws IOException{
HttpURLConnection con = null;
con = (HttpURLConnection)new URL("http://127.0.0.1:8080").openConnection();
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json; charset=utf-8");
con.setRequestProperty("Accept", "application/json; charset=utf-8");
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(body);
wr.flush();
wr.close();
...
}
I post it to localhost just to sniff it with WireShark.
The problem is that when my body is a string containing characters like 'ò' 'à' 'è' 'ç' ... the request i see has le string correct with those characters replaced by dots.
example:
if body is "hèllo!" ---> the request body is "h.llo!"
Just for test i'm executing the above method in java main and i pass the parameter this way:
String pString = "{\"titlè\":\"Hèllo Wòrld!\"}";
String params = new String(pString.getBytes("UTF-8"),"UTF-8");
....
call(body);
and this is what i get in WireShark:
POST / HTTP/1.1
Accept-Charset: UTF-8
Content-Type: application/json; charset=utf-8
Accept: application/json; charset=utf-8
User-Agent: Java/1.6.0_43
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 24
{"titl.":"H.llo W.rld!"}
Any help would be appreciated.
Thank you

The internal string representation in Java is always UTF-16. So in your second example params = new String(pString.getBytes("UTF-8"),"UTF-8"); converts pString to a byte array with UTF-8 content and then back to UTF-16 which is stored in params.
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.
wr.write(body.getBytes("UTF-8"));

Related

How to call JAX-WS method from URL

I have a local web service and I can call its methods using a JAVA client.
Is it possible to access its methods using a URL ?
I can access the wsdl XML using this URL:
http://localhost:9999/ws/hello?wsdl
And I would like to call a method like such:
http://localhost:9999/ws/hello/getHelloWorldAsString?name=test
But I am receiving errors "Localhost did not send any data".
Is there a way to do this ?
As far as I was aware Jax-ws uses POST to receive calls. You will have to build an XML request to POST to your URL. Something like this:
POST /ws/hello HTTP/1.1
SOAPAction: ""
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Content-Type: text/xml; charset=utf-8
User-Agent: Java/1.6.0_13
Host: localhost:9999
Connection: keep-alive
Content-Length: 224
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getHelloWorldAsString xmlns:ns2="http://ws.mkyong.com/">
<arg0>test</arg0>
</ns2:getHelloWorldAsString>
</S:Body>
</S:Envelope>
Use java.net.Url and HttpURLConnection or HTTPSURLConnection
look a sample
URL url = new URL("http://yourwebservices.soap.wsdl");
HttpURLConnection connectionWS = (HttpURLConnection) ur.openConnection();
//not forget this
connectionWS.setDoOutput(true);
connectionWS.setDoInput(true);
connectionWS.setRequestMethod("POST");
connectionMinervaWS.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
StringBuilder envelopeSoapRequest = new StringBuilder()
//make the xml request
//now you send to service
OutputStreamWriter osw = new OutputStreamWriter( connectionWS.getOutputStream() );
osw.write( envelopeSoapRequest.toString() );
osw.flush();
//now you can take response
BufferedReader wsReader = null;
StringBuilder envelopeSoapResponse = new StringBuilder();
wsReader = new BufferedReader(new InputStreamReader(
connectionWS.getInputStream(), StandardCharsets.UTF_8 ));
String line = wsReader.readLine();
while (line != null) {
envelopeSoapResponse.append( line );
line = wsReader.readLine();
}

How to write a very basic POST client in Java

I have to write the most basic POST client in Java.
It sends a few parameters to a Certificate server.
The parameters are supposed to be JSON encoded
I have the attached code, how do I make the params JSON encoded?
String url = "http://x.x.x.x/CertService/revoke.php";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
//add reuqest header
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
String urlParameters = "serialnumber=C02G8416DRJM&authtoken=abc&caauthority=def&reason=ghi";
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
Can you just set your content type as application/json and send the json string?
con.setRequestProperty("Content-Type", "application/json");
The text you're POSTing in your example looks like it's application/x-www-form-urlencoded. To get application/json encoding, prepare the parameters as a map, then use any of several JSON encoding libraries (e.g., Jackson) to convert it to JSON.
Map<String,String> params = new HashMap<>();
params.put("serialnumber", "C02G8416DRJM");
params.put("authtoken", "abc");
...
ObjectMapper mapper = new ObjectMapper();
OutputStream os = con.getOutputStream();
mapper.writeValue(os, params);
os.flush();
os.close();
...

Strange behaviour in Get request

NOTE: This contains fixed code.
The following get request works:
curl https://9d3d9934609d1a7d79865231be1ecb23:9432fb76a34a0d46d64a2f4cf81bebd6#smartprice-2.myshopify.com/admin/orders.json
But the following code in java that I though did the same returns a 401.
final String url = "https://9d3d9934609d1a7d79865231be1ecb23:9432fb76a34a0d46d64a2f4cf81bebd6#smartprice-2.myshopify.com/admin/orders.json";
final HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", USER_AGENT);
con.setRequestProperty("Content-Type", "application/json");
final String encoded = Base64.encodeBase64String((api+":"+pass).getBytes());
con.setRequestProperty("Authorization", "Basic "+encoded);
System.out.println("\nSending 'GET' request to URL : " + url);
int responseCode = con.getResponseCode();
System.out.println("Response Code : " + responseCode);
What am I missing here?
Are those not identical?
401 means unauthorized. Nothing surprising. The thing is that curl is able to resolve username:password used in your URL (part before '#' sign) and append it automatically as Authorization header in your request. But Java API is not doing this so you will have to do it on your own. The best way to investigate is to run curl with -v option. In it, you will see something like:
* Server auth using Basic with user '9d3d9934609d1a7d79865231be1ecb23'
> GET /admin/orders.json HTTP/1.1
> Host: smartprice-2.myshopify.com
> Authorization: Basic OWQzZDk5MzQ2MDlkMWE3ZDc5ODY1MjMxYmUxZWNiMjM6OTQzMmZiNzZhMzRhMGQ0NmQ2NGEyZjRjZjgxYmViZDY=
> User-Agent: curl/7.44.0
> Accept: */*
So you can notice that curl automatically appends HTTP Basic Authorization header to your request. So the correct Java code would be:
final String url = "https://smartprice-2.myshopify.com/admin/orders.json";
final HttpsURLConnection con = (HttpsURLConnection) new URL(url).openConnection();
con.setRequestProperty("Authorization", "Basic OWQzZDk5MzQ2MDlkMWE3ZDc5ODY1MjMxYmUxZWNiMjM6OTQzMmZiNzZhMzRhMGQ0NmQ2NGEyZjRjZjgxYmViZDY=");
con.setRequestMethod("GET");
System.out.println("Response Code : " + con.getResponseCode());
You can notice, that there is no reason to use credentials in URL and use only Authorization header (request property). By the way if you decode Base64: OWQzZDk5MzQ2MDlkMWE3ZDc5ODY1MjMxYmUxZWNiMjM6OTQzMmZiNzZhMzRhMGQ0NmQ2NGEyZjRjZjgxYmViZDY=, you will get exactly the part of URL before '#' which is: 9d3d9934609d1a7d79865231be1ecb23:9432fb76a34a0d46d64a2f4cf81bebd6
If you want automatic way how to resolve your Authorization header, you can use
final String credentials = DatatypeConverter.printBase64Binary("username:password".getBytes());
con.setRequestProperty("Authorization", "Basic " + credentials);
401 error stands for the Unauthorized access.
You need to either use Authenticator:
Authenticator.setDefault (new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication ("username", "password".toCharArray());
}
});
or set a property:
String basicAuth = "Basic " + new String(new Base64().encode(userpass.getBytes()));
con.setRequestProperty ("Authorization", basicAuth);

POSTing foreign characters using JSON produces 400

I'm trying to make a POST request using JSON with foreign characters, such as the Spanish n with the '~' over it, but I keep getting this request and response error:
POST ...
Accept: application/json
Content-Type: application/json; charset=UTF-8
Content-Length: 151
Content-Encoding: UTF-8
Host: ...
Connection: Keep-Alive
User-Agent: ..
{"numbers":"2","date":"2014-07-15T00:00:00+0000","description":" // this never gets closed
X-Powered-By: ...
Set-Cookie: ...
Cache-Control: ...
Date: Tue, 15 Jul 2014 15:19:12 GMT
Content-Type: application/json
Allow: GET, POST
{"status":"error",
"status_code":400,
"status_text":"Bad Request",
"current_content":"",
"message":"Could not decode JSON, malformed UTF-8 characters (incorrectly encoded?)"}
I can already make a successful POST request with normal ASCII characters, but now that I'm supporting foreign languages, I need to convert the foreign characters to UTF-8 (or whatever the correct encoding ends up being), unless there's a better way to do this.
Here's my code:
JSONObject jsonObject = new JSONObject();
HttpResponse resp = null;
String urlrest = // some url;
HttpPost p = new HttpPost(urlrest);
HttpClient hc = new DefaultHttpClient();
hc = sslClient(hc);
try
{
p.setHeader("Accept", "application/json");
p.setHeader("Content-Type", "application/json");
// setting TimeZone stuff
jsonObject.put("date", date);
jsonObject.put("description", description);
jsonObject.put("numbers", numbers);
String seStr = jsonObject.toString();
StringEntity se = new StringEntity(seStr);
// Answer: The above line becomes new StringEntity(seStr, "UTF-8");
Header encoding = se.getContentType();
se.setContentEncoding("UTF-8");
se.setContentType("application/json");
p.setEntity(se);
resp = hc.execute(p);
When I put a breakpoint and look at se before it's submitted, the characters look right.
UPDATE: code updated with answer a few lines above with a comment identifying it.
The new StringEntity constructor takes a "UTF-8" parameter.

java HttpsURLConnection

I tried to make this curl request executable from Java:
curl -H 'Accept: application/vnd.twitchtv.v2+json' \
-d "channel[status]=testing+some+stuff" \
-X PUT https://api.twitch.tv/kraken/channels/testacc222?oauth_token=6e7b9cyfi8zk1gr8g06eecebnitlcvb
My solution looks like this:
public static void main(String args[]) throws IOException {
String uri = "https://api.twitch.tv/kraken/channels/testacc222?oauth_token=6e7b9cyfi8zk1gr8g06eecebnitlcvb";
URL url = new URL(uri);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("PUT");
conn.setDoOutput(true);
conn.setRequestProperty("Accept", "application/vnd.twitchtv.v2+json");
String data = "channel[status]=testing";
OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
out.write(data);
out.flush();
for (Entry<String, List<String>> header : conn.getHeaderFields().entrySet()) {
System.out.println(header.getKey() + "=" + header.getValue());
}
}
I don't see any problem yet all it returns is:
Status=[400 Bad Request]
null=[HTTP/1.1 400 Bad Request]
Server=[nginx]
X-Request-Id=[ccc7a9a4a327b18ea4bf496f1f314fb8]
X-Runtime=[0.032328]
Connection=[keep-alive]
X-MH-Cache=[appcache1; M]
Date=[Sun, 06 Jul 2014 14:07:49 GMT]
Via=[1.1 varnish]
Accept-Ranges=[bytes]
X-Varnish=[2778442693]
X-UA-Compatible=[IE=Edge,chrome=1]
Cache-Control=[max-age=0, private, must-revalidate]
Vary=[Accept-Encoding]
Content-Length=[83]
Age=[0]
X-API-Version=[2]
Content-Type=[application/json; charset=utf-8]
I'm trying to figure this out for over a week now and I just don't see the mistake. Any help whatsoever would be greatly appreciated.
Try examining the response body, as it probably contains details about the rejection. Since the Content-Type specifies utf-8, you can create an InputStreamReader using that:
try (Reader response =
new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8)) {
int c;
while ((c = response.read()) >= 0) {
System.out.print((char) c);
}
}
Update: The response body states that the 'channel' parameter isn't present. This is because curl automatically encodes the POST data as application/x-www-form-urlencoded, but your code does not. You'll need to use URLEncoder on your data and also set the request's Content-Type:
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("PUT");
conn.setDoOutput(true);
conn.setRequestProperty("Accept", "application/vnd.twitchtv.v2+json");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
String data = "channel[status]=testing";
data = URLEncoder.encode(data, "UTF-8");

Categories

Resources