How to send query parameters to POST request from Java client, and receive using @QueryParam

19,742

Solution 1

To bind the value(s) of a HTTP query parameter to a resource method parameter using @QueryParam, you need to send your parameters in the query string of the URL:

3.4. Query

The query component contains non-hierarchical data that, along with data in the path component, serves to identify a resource within the scope of the URI's scheme and naming authority (if any). The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI.

[...]

By default, in GET requests, the parameters are sent as part of the URL. In POST requests, the parameters are sent in the body of the request.

To send query parameters in POST requests with HttpURLConnection, append them to the requested URL, as following:

List<NameValuePair> params = new ArrayList<>();
params.add(new NameValuePair("param1", "value1"));
params.add(new NameValuePair("param2", "value2"));

String query = getQuery(params);
URL url = new URL("http://localhost:8080/api" + "?" + query);
URLConnection connection = url.openConnection();

...

Solution 2

@QueryParam only recognizes parameters which are part of the URL. You need to use @FormParam instead in order to read the parameters from the POST body.

Solution 3

Note that rfc2616 is obsolete since 2014, it's replaced by the rfc723x specs. No rfc723x servers exists at this moment

A rfc723x compilant server will respond with status 400:

  • When a get request has a message body.
  • When a post request has parameters in the request uri.

Creating a server that will accept both are insecure by design. Uri parameters can and will be forged by a hacker. When a parameter is in the request uri and in the message body, how to securely handle this situation? Which has precedence?

The only reasonable answer is to reject such a request with status 400. You as a programmer cannot make the distinction between a legal user and a hacker in this situation.

rfc7230 also specifies that parameters may only occur once. Why? An example:

Who do you vote for? http://voting.com/?vote=clinton obviously Clinton

Who do you vote for? http://voting.com/?vote=trump&vote=clinton Trump? Clinton? Trump and Clinton?

Who do you vote for? http://voting.com/?vote=trump&vote=clinton&vote=trump Trump? Trump 2 times and Clinton 1 time?

rfc2616 servers accept this and return an array of values for vote and are insecure by design on this point.

Share:
19,742
Akhil Dixit
Author by

Akhil Dixit

Updated on June 23, 2022

Comments

  • Akhil Dixit
    Akhil Dixit almost 2 years

    I'm trying to write a basic web service that uses JAX-RS @QueryParam annotation. My Java client sends a POST request using HttpURLConnection. The call is being made to the web service, but the parameters are not getting sent correctly (They remain null). I know there are lot of questions related to this on StackOverflow, but I couldn't find a convincing solution. It'll be great if somebody points out the mistake in the code below. (I've simplified the code for posting here).

    Here's the web service code:

    @POST
    @Path("/notify")
    @Consumes("*/*")
    public Response notifyUser(@QueryParam("notification") String notification, @QueryParam("applicationName") String applicationName) {
        System.out.println("Notification: " + notification);
        System.out.println("Application: " + applicationName);
        return Response.status(200).entity(notification + " from " + applicationName).build();
    }
    

    And this is Java client:

    public static void main(String args[]) {
        URL url;
        HttpURLConnection conn = null;
        try {
             url = new URL("http://localhost:8080/...");
             conn = (HttpURLConnection) url.openConnection();
             conn.setDoOutput(true);
             conn.setRequestMethod("POST");
             conn.setDoInput(true);
             conn.setDoOutput(true);
             conn.setRequestProperty("Content-Type", "application/json");
             conn.setRequestProperty("charset", "UTF-8");
             OutputStream os = conn.getOutputStream();
             List<NameValuePair> params = new ArrayList<NameValuePair>();
             params.add(new NameValuePair("notification", "Notification string"));
             params.add(new NameValuePair("applicationName", "MyApp"));
             os.write(getQuery(params).getBytes("UTF-8"));
             os.flush();
             os.close();
             return conn.getResponseCode();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return -1;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }
    
    private String getQuery(List<NameValuePair> params) throws UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;
    
        for (NameValuePair pair : params) {
            if (first)
                first = false;
            else
                result.append("&");
    
            result.append(URLEncoder.encode(pair.getName(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(pair.getValue(), "UTF-8"));
        }
    
        return result.toString();
    }
    

    My web service is getting called, but the parameter values are received as null. Please help.