Context: In Android, when I use Java's HttpURLConnection object as shown below, I see the POST body correctly on the server side. However, when I use what I believe is the equivalent HttpClient code, the POST body is empty.
Question:
What am I missing?
Server-side is a Django-python server. I have set up a debug point at the entry point of this endpoint but the post body is already empty. How can I debug through it to find out why the body is null?
Note: I already looked at this , but the solution does not work for me.
Code: using HttpURLConnection - this works:
try {
URL url = new URL("http://10.0.2.2:8000/accounts/signup/");
String charset = "UTF-8";
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty ("Authorization", "Basic base64encodedstring==");
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded; charset=" + charset);
connection.setDoInput(true);
StringBuilder sb = new StringBuilder();
sb.append("appver=6&user=value1pw=&hash=h1");
OutputStreamWriter outputWriter = new
OutputStreamWriter(connection.getOutputStream());
outputWriter.write(sb.toString());
outputWriter.flush();
outputWriter.close();
// handle response
} catch () {
// handle this
}
============================================================
Code: using Apache httpclient - does NOT work - server gets empty POST body:
HttpPost mHttpPost = new HttpPost(""http://10.0.2.2:8000/accounts/signup/"");
mHttpPost.addHeader("Authorization", "Basic base64encodedstring==");
mHttpPost.addHeader("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
mHttpPost.addHeader("Accept-Charset", "UTF-8");
String str = "appver=6&user=value1pw=&hash=h1"; // same as the above
StringEntity strEntity = new StringEntity(str);
mHttpPost.setEntity(strEntity);
HttpUriRequest pHttpUriRequest = mHttpPost;
DefaultHttpClient client = new DefaultHttpClient();
httpResponse = client.execute(pHttpUriRequest);
// more code
I figured the reason why this was happening:
The authorization header in the POST request had an extra new line character "\n" - this was causing the request to go through to the server side handler, but with the body getting cut off. I have never noticed this behavior before.
Related
I am trying to move away from Apache HTTP to HttpUrlConnection in my Android app. I am stuck and I tried looking everywhere but I cannot get through it. Here is what I am trying.
Below is my HTTP code:
HttpParams timeoutParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(timeoutParams, 60000);
HttpConnectionParams.setSoTimeout(timeoutParams, 60000);
DefaultHttpClient httpClient = null;
httpClient = new DefaultHttpClient(timeoutParams);
Cookie podCookie = getPodCookie();
if (podCookie != null) {
httpClient.getCookieStore().addCookie(podCookie);
}
HttpPost postMethod = null;
postMethod.addHeader("Authorization", "<auth-header>");
try {
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
for (Entry<String, String> entry : parameters.entrySet()) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
String queryString = URLEncodedUtils.format(nvps, HTTP.UTF_8);
String modUrl = url + "?" + queryString;
postMethod = new HttpPost(modUrl);
StringEntity entity = new StringEntity(<String JSON to send>, HTTP.UTF_8);
postMethod.setEntity(entity);
postMethod.setHeader("Content-type", "application/json");
HttpResponse reply = httpClient.execute(postMethod);
This is the HttpUrlConnection equivalent of the above code:
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
for (Entry<String, String> entry: parameters.entrySet()) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
String queryString = URLEncodedUtils.format(nvps, HTTP.UTF_8);
String modUrl = baseUrl + "?" + queryString;
HttpURLConnection urlConnection = null;
try {
URL url1 = new URL(modUrl);
urlConnection = (HttpURLConnection) url1.openConnection();
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);
cookieManager.getCookieStore().add(new URI(url), podCookie);
urlConnection.setConnectTimeout(60000);
urlConnection.setReadTimeout(60000);
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Authorization", <auth-header>);
urlConnection.setRequestProperty("Content-Type", "application/json");
OutputStream os = urlConnection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
writer.write(<String JSON to send>);
writer.flush()
writer.close();
os.close();
InputStream is = new BufferedInputStream(urlConnection.getInputStream());
String responseString = WebService.convertInputStreamToString(is);
When I try the above, I get a 401 Unauthorized error. I am using Charles and the headers are the same.
When I try to add the query params in the BufferedWriter instead of the URL I change the url to the base url, like this:
URL url1 = new URL(baseUrl);
and add the following line to writer, like this:
writer.write(modUrl)
When I do this, I get a 500 Internal Server Error.
In both the cases, I am getting an IOException which is a FileNotFoundException on the InputStream line.
Any ideas on how to solve this?
you should dump the headers from the successful httpclient call so you know exactly what headers are being sent with OK request.
Its not clear how you are setting "Authorization" header
Its not clear what Json value is being set into the 'StringEntity'.
you should know exactly what is being sent in a good call ( httpClient call or Curl CLI call ) before you try to use the HttpUrlConnection. Then set the same headers over there, write the same JSON to the Connections' outputStream and you should get the same result.
Curl expression for your two headers and with your JSON Entity in the curl.-d switches value....
curl -v -X POST \
-H "Content-type: application/json" \
-H "Authorization: aValueforAuth" \
-d '{"score":1337,"playerName":"Sean Plott",...}' \
http://domain/path?parm1=$urlEncodedVal-1&parm2=$urlEncodedVal-2
i have a RESTful WCF service and one of its methods use an Object as parameter
[WebInvoke(UriTemplate = "save", Method = "POST", RequestFormat = WebMessageFormat.Xml, ResponseFormat= WebMessageFormat.Xml), OperationContract]
public SampleItem Create(SampleItem instance)
{
return new SampleItem() { Id = 1, StringValue = "saved" };
// TODO: Add the new instance of SampleItem to the collection
//throw new NotImplementedException();
}
I am trying to call this method from my eclipse android project. i am using these lines of codes
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost post=new HttpPost("http://10.0.2.2:2768/Service1.svc/save");
ArrayList<NameValuePair> nvp= new ArrayList<NameValuePair>();
nvp.add(new BasicNameValuePair("Id", "1"));
nvp.add(new BasicNameValuePair("StringValue", "yolo"));
post.setEntity(new UrlEncodedFormEntity(nvp));
HttpResponse httpResponse = httpClient.execute(post);
HttpEntity httpEntity = httpResponse.getEntity();
String xml = EntityUtils.toString(httpEntity);
Every time i get this error Method not allowed. in the XML that is returned by the service method.
i have tried invoking it from the browser, but encountered the same error there.
please tell me what i am doing wrong and what i can do instead.
thanks in advance to anyone who can help.
note: other methods which do not use object as parameter are working fine.
EDIT: tried Fiddler2 with success. but stalled again.
i have tried invoking the method SampleItem Create(SampleItem instance) with the url http://localhost:2768/Service1.svc/save and it works. the method returns the object in XML format.
in fiddler i added the request body as
<SampleItem xmlns="http://schemas.datacontract.org/2004/07/WcfRestService1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Id>1</Id><StringValue>saved</StringValue></SampleItem>
but the problem is that i can not find any way to add this xml string to the HttpPost or HttpRequest as the requestbody eclipse android project.
note: passing the xml string as Header or UrlEncodedFormEntity did not work.
finally i have succeeded to send a json object over to my WCF Service here's my code
URI uri = new URI("http://esimsol.com/droidservice/pigeonlibrary.service1.svc/save");
JSONObject jo1 = new JSONObject();
jo1.put("Id", "4");
jo1.put("StringValue", "yollo");
HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection();
conn.setRequestProperty("Content-Type","application/json; charset=utf-8");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("User-Agent", "Pigeon");
conn.setChunkedStreamingMode(0);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.connect();
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.write(jo1.toString().getBytes());
out.flush();
int code = conn.getResponseCode();
String message = conn.getResponseMessage();
InputStream in = conn.getInputStream();
StringBuffer sb = new StringBuffer();
String reply;
try {
int chr;
while ((chr = in.read()) != -1) {
sb.append((char) chr);
}
reply = sb.toString();
} finally {
in.close();
}
SampleItem SI = new SampleItem();
SI=new Gson().fromJson(reply, SampleItem.class);
Toast.makeText(getApplicationContext(), SI.getStringValue(),Toast.LENGTH_LONG).show();
conn.disconnect();
thanks to StackOverFlow.
i had to combine a number of code snippets to achieve this.
First, you should get the Web Service method working from the browser - I recommend using Fiddler2 - its easier to construct the request body with your object and also to set the request headers when doing a post. It will show you the response so should help with debugging.
As for your code, I'm doing a POST to a WCF service and instead of doing
post.setEntity(new UrlEncodedFormEntity(nvp));
I'm simply doing:
HttpPost request = new HttpPost(url);
// Add headers.
for(NameValuePair h : headers)
{
request.addHeader(h.getName(), h.getValue());
}
(I am using JSONObjects and I have RequestFormat = WebMessageFormat.Json in my WebInvoke parameters.
Also, check your using the correct UriTemplate name in your url as they are case sensitive.
To call that WCF service you must build valid SOAP request and post it. It is better to use some SOAP protocol stack on Android - for example kSoap2.
Here is example of using kSoap2 to call WCF service.
just add KSOAP2 lib in your project.for how we add KSOAP2 in Android project see this post
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
I want to send my id & password to server and get the response from server. Here is my code. It is not working for the first time. But iam getting the response from server if i execute my application on second time. It is throwing "Post method failed: -1 null" on first time. Where iam wrong?? Why if() block is executing on first time?? could you please tell me.
HttpsURLConnection con = null;
String httpsURL = "https://www.abc.com/login";
String query = "id=xyz&password=pqr";
URL url = new URL(httpsURL);
con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-length", String.valueOf(query.length()));
con.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
con.setRequestProperty("User-Agent","Mozilla/4.0(compatible; MSIE 5.0; Windows 98; DigExt)");
con.setDoInput(true);
con.setDoOutput(true);
DataOutputStream output = new DataOutputStream(con.getOutputStream());
output.writeBytes(query);
output.close();
int respCode = con.getResponseCode();
if (respCode != HttpsURLConnection.HTTP_OK)
{
throw new Exception("POST method failed: " + con.getResponseCode()+ "\t" + con.getResponseMessage()); }
else {
//read the content from server
}
1/ It is recommanded to use apache HttpClient rather than URLConnection (see http://developer.android.com/reference/org/apache/http/impl/client/DefaultHttpClient.html)
2/ for login and password, why not use Http Authentication ? both basic and digest are supported by android.
3/ as for you problem, you don't close the underlying outputStream.
you should do:
OutputStream os = con.getOutputStream();
DataOutputStream output = new DataOutputStream(os);
output.writeBytes(query);
output.close();
os.close();
Check Server service validity with other technology and/or classic java. You didn say in your question if you succeed to discriminate the server from the issue.
from java doc ...getResponseCode returns -1 if no code can be discerned from the response (i.e., the response is not valid HTTP).
Java https post request example : http://www.java-samples.com/java/POST-toHTTPS-url-free-java-sample-program.htm
try to close your outputstream after querying the status and not before...that may help
Here is how you should send POST requests in Android
HttpPost httpGet = new HttpPost(server + "/login?email="+username+"&password="+password);
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(httpGet);
You can read response using:
response.getEntity().getContent()
I am able to do a POST of a parameters string. I use the following code:
String parameters = "firstname=john&lastname=doe";
URL url = new URL("http://www.mywebsite.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
connection.setRequestMethod("POST");
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
out.write(parameters);
out.flush();
out.close();
connection.disconnect();
However, I need to do a POST of binary data (which is in form of byte[]).
Not sure how to change the above code to implement it.
Could anyone please help me with this?
Take a look here
Sending POST data in Android
But use ByteArrayEntity.
byte[] content = ...
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new ByteArrayEntity(content));
HttpResponse response = httpClient.execute(httpPost);
You could base-64 encode your data first. Take a look at the aptly named Base64 class.
These links might be helpful:
Android httpclient file upload data corruption and timeout issues
http://getablogger.blogspot.com/2008/01/android-how-to-post-file-to-php-server.html
http://forum.springsource.org/showthread.php?108546-How-do-I-post-a-byte-array