how to properly make a POST request using Java Apache HttpClient? - java

I am trying to use a web API in a Java program using Apache HttpClient5.
Using a simple request with curl:
curl -X POST -H "x-api-user: d904bd62-da08-416b-a816-ba797c9ee265" -H "x-api-key: xxxxxxxxxxx" https://habitica.com/api/v3/user/class/cast/valorousPresence
I get the expected response and effect.
Using my Java code:
URI uri = new URIBuilder()
.setScheme("https")
.setHost("habitica.com")
.setPath("/api/v3/user/class/cast/valorousPresence")
.build();
Logger logger = LoggerFactory.getLogger(MyClass.class);
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(uri);
httpPost.addHeader(new BasicHeader("x-api-user",getApiUser()));
httpPost.addHeader(new BasicHeader("x-api-key", getApiKey()));
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
logger.info(httpResponse.toString());
return httpResponse.getCode();
The output I get when running the Java call is
411 Length Required HTTP/1.0
I'm sure I'm not constructing the POST call correctly, how should it be done? I've tried specifying Content-Type and that has no effect. Trying to set Content-Length in the code causes compilation errors (as I understand it, this is handled behind the scenes by HttpClient5).
All my GET requests using HttpClient5 work fine.

A POST always has a payload (content). A POST without content is unusual, so are you sure you didn't forget something?
You need to call setEntity() to set the payload, even if it is empty, because it is the entity that sets the Content-Length header.
E.g. you could call httpPost.setEntity(new StringEntity("")), which sets Content-Type: text/plain and Content-Length: 0.

Related

POST request to server using java URLConnnection with params and file inputs

POST request to server using java URLConnnection
I need to send a POST request with the two parameters below:
param1=value1
param2=value2
And also I need to send a file.
In the case of Apache these 2 two(sending params and file) things are handled like below
post.setQueryString(queryString) // queryString is url encoded for eg: param1=value1&param2=value2
post.setRequestEntity(entity) // entity is constructed using file input stream with corresponding format
Please let me know if you have anything related to this problem.
Please note: When I try using Google Chrome REST client plug-in, I am getting the response as below (tried with all request content-types)
UNSUPPORTED FILE FORMAT: 'multipart/form-data' is not a supported content-type
Response code is 400.
Try this API from Apache to send request internally with POST method.
The below is the sample Code to use API
List<org.apache.http.NameValuePair> list =new ArrayList<org.apache.http.NameValuePair>();
HttpPost postMethod = new HttpPost("http://yoururl/ProjectName");
list.add(new BasicNameValuePair("param1", "param1 Value")) ;
postMethod.setEntity(new UrlEncodedFormEntity(list));
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(postMethod);
InputStream is = response.getEntity().getContent();

HTTP Bad Request response to Java POST call

I'm attempting to query a REST api using POST requests in a java application. I think I've set everything correctly, but I keep getting a Bad Request response.
HttpPost request = new HttpPost(requestURI);
request.addHeader("accept", "application/json");
request.addHeader(HttpHeaders.CONTENT_TYPE, "application/json");
HttpEntity entity = new StringEntity(requestBody + new Integer(PatientId).toString() + "}");
request.setEntity(entity);
The requestBody, accompanied by the number and curly brace, are valid JSON, and the requestURI is copy and pasted straight out of the API documentation, so I know I shouldn't be getting a Bad Request due to those.
Am I missing something in the setup?
The Content-Length header is missing. Some servers don't report the correct 4xx error (411 Length Required) and just issue a generic Bad Request error.
It ended up being a random slash that wasn't included in my URI.

How can I disable default request headers from apache httpclient 4?

I am using apache common httpclient 4.3.3 to make http 1.0 request. Here is how I make the request
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url);
post.setProtocolVersion(new ProtocolVersion("HTTP", 1, 0));
// trying to remove default headers but it doesn't work
post.removeHeaders("User-Agent");
post.removeHeaders("Accept-Encoding");
post.removeHeaders("Connection");
post.setEntity(new ByteArrayEntity(ba) );
HttpResponse response = client.execute(post);
However, i can see that there are other headers automatically added to my request to the server like
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.3.3 (java 1.5)
Accept-Encoding: gzip,deflate
How can I tell httpclient not to include any other headers? I tried to removed those headers with post.removeHeaders(xxxx) but it doesn't work. Can you show me how?
Thanks,
If you call HttpClientBuilder.create(), you will have a httpClientBuilder.
And httpClientBuilder has a lot config for default headers and this will be used to make intercepters( ex: RequestAcceptEncoding ).
For example, RequestAcceptEncoding, which implements HttpRequestInterceptor, makes Accept-Encoding: gzip,deflate header when HttpProcessor.process() is invoked.
And httpProcessor.process() will be invoked just before invoking
final CloseableHttpResponse response = this.requestExecutor.execute(route, request, context, execAware);
You can see this code at org.apache.http.impl.execchain.ProtocolExec of httpclient-4.3.6 line 193.
If you want to remove Accept-Encoding: gzip,deflate, call HttpClientBuilder.disableContentCompression() like below.
HttpClient client = HttpClientBuilder.create().disableContentCompression().build();
In short, HttpClientBuilder has a lot of flags to disable/enable HttpRequestInterceptor. If you disable/enable those HttpRequestInterceptor, you can exclude/include default headers.
Sorry for my poor English, and hope you get what I mean.
CloseableHttpClient hc = HttpClients.custom()
.setHttpProcessor(HttpProcessorBuilder.create().build())
.build();
The code snippet above demonstrates how to create an HttpClient instance with an empty (no-op) protocol processor, which guarantees no request headers will ever be added to outgoing messages executed by such client
You want to do your 'cleanup' at the end, after HttpClient is done modifying the request. You can do this by calling addInterceptorLast on HttpClientBuilder as below.
HttpClient client = HttpClientBuilder.create().addInterceptorLast(
new HttpRequestInterceptor() {
public void process(HttpRequest request, HttpContext context){
request.removeHeaders("Host");
request.removeHeaders("Connection");
}
}
).build();
I create an anonymous class implementing HttpRequestInterceptor. Take whatever header modifications you need done before the request is sent, and put them in the process method.
I think you could add your implementation of HttpRequestInterceptor with client.addRequestInterceptor()
or (better)
remove all interceptors that add headers to the request (RequestUserAgent, RequestDefaultHeaders, RequestClientConnControl, RequestAcceptEncoding, ...).
Removing them is also easy:
client.removeRequestInterceptorByClass(RequestAcceptEncoding.class);

Request sent with version Http/0.9

I am using Apache Commons HttpClient v3.1. All my requests are having correct (default) HTTP version in the request line i.e HTTP/1.1 except for 1 request.
Following Post request gets the requestline as HTTP/0.9:
server : port/cas/v1/tickets/TGT-1-sUqenNbqUzvkGSWW25lcbaJc0OEcJ6wg5DOj3XDMSwoIBf6s7i-cas-1
Body: service=*
I debugged through the http client code and saw the requestline is set to HTTP/1.1 but on the server I see the request coming as HTTP/0.9.
I tried to set the HTTP version explicitly using the HttpMethodParams but that does not help.
Does anyone have an idea what could be wrong?
HttpClient client = new HttpClient();
HostConfiguration hc = client.getHostConfiguration();
hc.setHost(new URI(url, false));
PostMethod method = new PostMethod();
method.setURI(new URI(url, false));
method.getParams().setUriCharset("UTF-8");
method.getParams().setHttpElementCharset("UTF-8");
method.getParams().setContentCharset("UTF-8");
method.getParams().setVersion(HttpVersion.HTTP_1_1);
method.addParameter("service", URLEncoder.encode(service, "UTF-8"));
method.setPath(contextPath + "/tickets/" + tgt);
String respBody = null;
int statusCode = client.executeMethod(method);
respBody = method.getResponseBodyAsString();
Thanks Joachim Sauer. I was able to figure out the problem.
I was using Webscarab as web proxy and it emitted out following message "Got a continuation header but had no previous header line". Looked it up online and found that the problem was in multi-line requestline. I was setting the HTTP version to 1.1 explicitly however there was a trailing '\r\n' in the url which made the requestline look like Http/0.9.
Difference between Http/0.9 and Http/1.0 or other higher protocols is that 0.9 had a simple requestline 'METHOD URL'. Later versions also include the Http version in the request line 'METHOD URL HTTPVERSION'.
Hope it saves someone day!

Android HttpPost message won't send its payload across the wire

I'm trying to send a simple string as the contents of a HttpPost message.
The problem is, the body of the HttpPost message never makes it to the wire. (Says the Wireshark capture). The header looks just fine though (including the correctly calculated Content-Length.
Here's what the code looks like:
String url = "http://1.2.3.4/resource";
HttpClient client = new DefaultHttpClient();
String cmd = "AT+AVLPOS\r\n";
StringEntity se = new StringEntity(cmd);
se.setContentType("text/plain");
HttpPost request = new HttpPost(url);
request.setHeader("Content-Type","text/plain");
request.setEntity(se);
HttpResponse response = client.execute(request);
[...]
The string should be ASCII-encoded, but that's a detail at this point.
This is what shows up in WireShark:
-> note that lines marked with + are what's sent, and - is what's received.
+POST /resource HTTP/1.1
+Content-Type: text/plain
+Content-Length: 11
+Host: 1.2.3.4
+Connection: Keep-Alive
+User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
+Expect: 100-Continue
-HTTP/1.1 200 OK
-Content-Type: text/plain
-Transfer-Encoding: chunked
-4
-OK
This is what should show up (wrote a very simple console app in C# to do this, it just works):
+POST /resource HTTP/1.1
+Content-Type: text/plain
+Host: 1.2.3.4
+Content-Length: 11
+Expect: 100-continue
+Connection: Keep-Alive
+
-HTTP/1.1 200 OK
-Content-Type: text/plain
-Transfer-Encoding: chunked
-
+AT+AVLPOS
+
-4
-OK
-
-48
-$AVTMR,99999999,204810,A,1234.2218,N,0123.1051,E,0,20,150811,0,REQ*69
-
-0
-
Any suggestions?
I've figured it out, AND I've learned something today.
Long story short: disable the HttpClient's HTTP Post expect-continue handshake, by setting one of its parameters, this will send the whole request message in one chunk.
//set up HttpPost request as before
HttpClient client = new DefaultHttpClient();
client.getParams().setBooleanParameter("http.protocol.expect-continue", false);
HttpResponse response = client.execute(request);
[...]
Now here's how I got there, maybe this will help someone someday.
First I derived from a HttpEntityWrapper and used that as my request entity to see what gets called when, and found out that the Entity's writeTo(OutputStream) method was never called at all.
Then I started to look at why, in the case of the "correct" behaviour, the POST request wasn't sent all at once, and instead, the request headers were sent, then the response header is received, THEN the request body is sent.
IT's all got to do with the HTTP Post expect-continue handshake. Read more about it on Haacked.
If the expect-continue header is sent in a request, the Http server SHOULD reply with a 100 Continue message signifying "OK, I will accept your message", or with an error, stopping the possibly long POST message in its tracks.
Unfortunately, the web server I run against is a bare bones implementation that runs on a chip, and it sends the wrong reply (200 OK instead of 100 Continue).
The default implementation of the .NET Http Client seems to be more forgiving here: it treats the 200 message as 100 Continue, shrugs, and gets on its way to send the request body.
Not so with the Http client implementation of Android (API level 7).
Next thing I tried was to disable the expect-continue handshake completely, in order to make the HttpClient send the whole request. To my surprise and joy, this was handled fine by the web server, which replied with the information I wanted. Yay!

Categories

Resources