59

I am using a RESTfull webservice with this methode:

@POST
@Consumes({"application/json"})
@Path("create/")
public void create(String str1, String str2){
System.out.println("value 1 = " + str1);
System.out.println("value 2 = " + str2);
}

In my Android app I want to call this method. How do I give the correct values to the parameters using org.apache.http.client.methods.HttpPost;

I have noticed that I can use the annotation @HeaderParam and simply add headers to the HttpPost object. Is this the correct way? Doing it like:

httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("str1", "a value");
httpPost.setHeader("str2", "another value");

Using the setEntity methode on httpPost won't work. It only sets the parameter str1 with the json string. When using it like:

JSONObject json = new JSONObject();
json.put("str1", "a value");
json.put("str2", "another value");
HttpEntity e = new StringEntity(json.toString());
httpPost.setEntity(e);
//server output: value 1 = {"str1":"a value","str2":"another value"} 
Klaasvaak
  • 5,430
  • 12
  • 43
  • 67

3 Answers3

112

To set parameters to your HttpPostRequest you can use BasicNameValuePair, something like this :

    HttpClient httpclient;
    HttpPost httpPost;
    ArrayList<NameValuePair> postParameters;
    httpclient = new DefaultHttpClient();
    httpPost = new HttpPost("your login link");


    postParameters = new ArrayList<NameValuePair>();
    postParameters.add(new BasicNameValuePair("param1", "param1_value"));
    postParameters.add(new BasicNameValuePair("param2", "param2_value"));

    httpPost.setEntity(new UrlEncodedFormEntity(postParameters, "UTF-8"));

    HttpResponse response = httpclient.execute(httpPost);
Michael
  • 33,344
  • 15
  • 70
  • 105
Android-Droid
  • 13,649
  • 37
  • 107
  • 184
  • When I do this with and without @QueryParam annotation. Both parameters and null in the webapp. – Klaasvaak Nov 17 '11 at 11:03
  • you don't need any annotations to this. just type your param name and value like : `debug_data=1` or `username_hash=jhjahbkzjxcjkahcjkzhbcjkzhbxcjshd` I'm using this code with params and there is no problem for me. – Android-Droid Nov 17 '11 at 12:03
  • without annotations is the values are also null. – Klaasvaak Nov 17 '11 at 12:22
  • So the problem should be in your code and the way you put the values. – Android-Droid Nov 17 '11 at 13:54
  • When doing it like this with your code then method in the webapplication is called. The first thing it does is print the values to the logger. It says they are null. I doubt that has anything to do with my webapp code. – Klaasvaak Nov 19 '11 at 13:14
  • No i mean update your question with the code you are using right now – Android-Droid Nov 19 '11 at 13:30
  • Sorry for misunderstanding. It probably is my webapp code. I asked the question differently here: http://stackoverflow.com/questions/8194408/how-to-acces-paramaters-in-a-restfull-post-method – Klaasvaak Nov 19 '11 at 13:37
  • @Android-Droid Can you tell me maximum size of that array list or any collection that can be sent through setEntity()? – Newinjava Jun 25 '15 at 11:09
  • @Android-Droid Does it work for https post request too? – enjal Dec 08 '15 at 05:05
  • Make sure to set the content type... something like: .addHeader("Content-Type", "application/x-www-form-urlencoded") – comfytoday Nov 22 '17 at 01:06
7

You can also use this approach in case you want to pass some http parameters and send a json request:

(note: I have added in some extra code just incase it helps any other future readers)

public void postJsonWithHttpParams() throws URISyntaxException, UnsupportedEncodingException, IOException {

    //add the http parameters you wish to pass
    List<NameValuePair> postParameters = new ArrayList<>();
    postParameters.add(new BasicNameValuePair("param1", "param1_value"));
    postParameters.add(new BasicNameValuePair("param2", "param2_value"));

    //Build the server URI together with the parameters you wish to pass
    URIBuilder uriBuilder = new URIBuilder("http://google.ug");
    uriBuilder.addParameters(postParameters);

    HttpPost postRequest = new HttpPost(uriBuilder.build());
    postRequest.setHeader("Content-Type", "application/json");

    //this is your JSON string you are sending as a request
    String yourJsonString = "{\"str1\":\"a value\",\"str2\":\"another value\"} ";

    //pass the json string request in the entity
    HttpEntity entity = new ByteArrayEntity(yourJsonString.getBytes("UTF-8"));
    postRequest.setEntity(entity);

    //create a socketfactory in order to use an http connection manager
    PlainConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
    Registry<ConnectionSocketFactory> connSocketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", plainSocketFactory)
            .build();

    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(connSocketFactoryRegistry);

    connManager.setMaxTotal(20);
    connManager.setDefaultMaxPerRoute(20);

    RequestConfig defaultRequestConfig = RequestConfig.custom()
            .setSocketTimeout(HttpClientPool.connTimeout)
            .setConnectTimeout(HttpClientPool.connTimeout)
            .setConnectionRequestTimeout(HttpClientPool.readTimeout)
            .build();

    // Build the http client.
    CloseableHttpClient httpclient = HttpClients.custom()
            .setConnectionManager(connManager)
            .setDefaultRequestConfig(defaultRequestConfig)
            .build();

    CloseableHttpResponse response = httpclient.execute(postRequest);

    //Read the response
    String responseString = "";

    int statusCode = response.getStatusLine().getStatusCode();
    String message = response.getStatusLine().getReasonPhrase();

    HttpEntity responseHttpEntity = response.getEntity();

    InputStream content = responseHttpEntity.getContent();

    BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
    String line;

    while ((line = buffer.readLine()) != null) {
        responseString += line;
    }

    //release all resources held by the responseHttpEntity
    EntityUtils.consume(responseHttpEntity);

    //close the stream
    response.close();

    // Close the connection manager.
    connManager.close();
}
Arthur
  • 498
  • 6
  • 14
1

Generally speaking an HTTP POST assumes the content of the body contains a series of key/value pairs that are created (most usually) by a form on the HTML side. You don't set the values using setHeader, as that won't place them in the content body.

So with your second test, the problem that you have here is that your client is not creating multiple key/value pairs, it only created one and that got mapped by default to the first argument in your method.

There are a couple of options you can use. First, you could change your method to accept only one input parameter, and then pass in a JSON string as you do in your second test. Once inside the method, you then parse the JSON string into an object that would allow access to the fields.

Another option is to define a class that represents the fields of the input types and make that the only input parameter. For example

class MyInput
{
    String str1;
    String str2;

    public MyInput() { }
      //  getters, setters
 }

@POST
@Consumes({"application/json"})
@Path("create/")
public void create(MyInput in){
System.out.println("value 1 = " + in.getStr1());
System.out.println("value 2 = " + in.getStr2());
}

Depending on the REST framework you are using it should handle the de-serialization of the JSON for you.

The last option is to construct a POST body that looks like:

str1=value1&str2=value2

then add some additional annotations to your server method:

public void create(@QueryParam("str1") String str1, 
                  @QueryParam("str2") String str2)

@QueryParam doesn't care if the field is in a form post or in the URL (like a GET query).

If you want to continue using individual arguments on the input then the key is generate the client request to provide named query parameters, either in the URL (for a GET) or in the body of the POST.

Jere
  • 571
  • 2
  • 3
  • There is also a problem that the string sent can have chars like / < & %20. This won't work with QueryParam right? I noticed it does work with HeaderParam. – Klaasvaak Nov 14 '11 at 14:56
  • Also when I want to let the methode also consume XML, this won't work like this. – Klaasvaak Nov 14 '11 at 15:27
  • All strings included as a value for a Query parameter are expected to be properly URL encoded, so if you are constructing the post as mentioned it is expected that the values will have been URL encoded. So yes you can send XML, you just need to first run it through a URL encoding mechanism. That is what your browser would do if you had a text area on a form and entered XML. – Jere Nov 14 '11 at 18:01
  • Doesn't a URL encoder makes " " a "+". And more like that? Then that won't work because I also want to be able to send a "+". – Klaasvaak Nov 14 '11 at 18:25
  • Read up on the java.net.URLEncoder class it will explain what it does. Basically it will encode spaces to + but will encode plus signs in your text to their %xx equivalent, so it handles those details. If you set the content type of the send to application/x-www-form-urlencoded the REST package will automatically decode it for you. – Jere Nov 14 '11 at 20:53
  • The way I've seen using multiple arguments as you present is to use a form post format (key/value pairs) with each argument defined as a key and the value of each argument URLEncoded, regardless of value content. Then you can use the QueryParam in the method signature to map inbound fields to your arguments. Just setting the post body as a single string makes the server think it only received one argument. – Jere Nov 14 '11 at 21:09