How to access parameters in a RESTful POST method

313,551

Your @POST method should be accepting a JSON object instead of a string. Jersey uses JAXB to support marshaling and unmarshaling JSON objects (see the jersey docs for details). Create a class like:

@XmlRootElement
public class MyJaxBean {
    @XmlElement public String param1;
    @XmlElement public String param2;
}

Then your @POST method would look like the following:

@POST @Consumes("application/json")
@Path("/create")
public void create(final MyJaxBean input) {
    System.out.println("param1 = " + input.param1);
    System.out.println("param2 = " + input.param2);
}

This method expects to receive JSON object as the body of the HTTP POST. JAX-RS passes the content body of the HTTP message as an unannotated parameter -- input in this case. The actual message would look something like:

POST /create HTTP/1.1
Content-Type: application/json
Content-Length: 35
Host: www.example.com

{"param1":"hello","param2":"world"}

Using JSON in this way is quite common for obvious reasons. However, if you are generating or consuming it in something other than JavaScript, then you do have to be careful to properly escape the data. In JAX-RS, you would use a MessageBodyReader and MessageBodyWriter to implement this. I believe that Jersey already has implementations for the required types (e.g., Java primitives and JAXB wrapped classes) as well as for JSON. JAX-RS supports a number of other methods for passing data. These don't require the creation of a new class since the data is passed using simple argument passing.


HTML <FORM>

The parameters would be annotated using @FormParam:

@POST
@Path("/create")
public void create(@FormParam("param1") String param1,
                   @FormParam("param2") String param2) {
    ...
}

The browser will encode the form using "application/x-www-form-urlencoded". The JAX-RS runtime will take care of decoding the body and passing it to the method. Here's what you should see on the wire:

POST /create HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 25

param1=hello&param2=world

The content is URL encoded in this case.

If you do not know the names of the FormParam's you can do the following:

@POST @Consumes("application/x-www-form-urlencoded")
@Path("/create")
public void create(final MultivaluedMap<String, String> formParams) {
    ...
}

HTTP Headers

You can using the @HeaderParam annotation if you want to pass parameters via HTTP headers:

@POST
@Path("/create")
public void create(@HeaderParam("param1") String param1,
                   @HeaderParam("param2") String param2) {
    ...
}

Here's what the HTTP message would look like. Note that this POST does not have a body.

POST /create HTTP/1.1
Content-Length: 0
Host: www.example.com
param1: hello
param2: world

I wouldn't use this method for generalized parameter passing. It is really handy if you need to access the value of a particular HTTP header though.


HTTP Query Parameters

This method is primarily used with HTTP GETs but it is equally applicable to POSTs. It uses the @QueryParam annotation.

@POST
@Path("/create")
public void create(@QueryParam("param1") String param1,
                   @QueryParam("param2") String param2) {
    ...
}

Like the previous technique, passing parameters via the query string does not require a message body. Here's the HTTP message:

POST /create?param1=hello&param2=world HTTP/1.1
Content-Length: 0
Host: www.example.com

You do have to be particularly careful to properly encode query parameters on the client side. Using query parameters can be problematic due to URL length restrictions enforced by some proxies as well as problems associated with encoding them.


HTTP Path Parameters

Path parameters are similar to query parameters except that they are embedded in the HTTP resource path. This method seems to be in favor today. There are impacts with respect to HTTP caching since the path is what really defines the HTTP resource. The code looks a little different than the others since the @Path annotation is modified and it uses @PathParam:

@POST
@Path("/create/{param1}/{param2}")
public void create(@PathParam("param1") String param1,
                   @PathParam("param2") String param2) {
    ...
}

The message is similar to the query parameter version except that the names of the parameters are not included anywhere in the message.

POST /create/hello/world HTTP/1.1
Content-Length: 0
Host: www.example.com

This method shares the same encoding woes that the query parameter version. Path segments are encoded differently so you do have to be careful there as well.


As you can see, there are pros and cons to each method. The choice is usually decided by your clients. If you are serving FORM-based HTML pages, then use @FormParam. If your clients are JavaScript+HTML5-based, then you will probably want to use JAXB-based serialization and JSON objects. The MessageBodyReader/Writer implementations should take care of the necessary escaping for you so that is one fewer thing that can go wrong. If your client is Java based but does not have a good XML processor (e.g., Android), then I would probably use FORM encoding since a content body is easier to generate and encode properly than URLs are. Hopefully this mini-wiki entry sheds some light on the various methods that JAX-RS supports.

Note: in the interest of full disclosure, I haven't actually used this feature of Jersey yet. We were tinkering with it since we have a number of JAXB+JAX-RS applications deployed and are moving into the mobile client space. JSON is a much better fit that XML on HTML5 or jQuery-based solutions.

Share:
313,551
Klaasvaak
Author by

Klaasvaak

Updated on May 12, 2020

Comments

  • Klaasvaak
    Klaasvaak almost 4 years

    My POST method looks like this:

    @POST
    @Consumes({"application/json"})
    @Path("create/")
    public void create(String param1, String param2){
        System.out.println("param1 = " + param1);
        System.out.println("param2 = " + param2);
    }
    

    When I create a Jersey Client in Netbeans the method who calls the post method looks like this:

    public void create(Object requestEntity){
        webResource.path("create").type(MediaType.APPLICATION_JSON).post(requestEntity);
    }
    

    When running this test:

    @Test
    public void hello(){
        String json = "{param1=\"hello\",param2=\"hello2\"}";
        this.client.create(json);
    }
    

    It gives the following output in the server:

    INFO: param1 = {param1="hello",param2="hello2"}
    INFO: param2 = 
    

    What do I need to change so that the parameters are giving the correct value?

  • mmatloka
    mmatloka over 12 years
    I've tested this feature yesterday, @XmlElement annotations wasnt needed
  • Klaasvaak
    Klaasvaak over 12 years
    Thanks for this. If I still want to send 2 Strings then what does the restfull needs to be able to consume? Or do multiple params only work with @PathParm?
  • Perception
    Perception over 12 years
    @Klaasvaak - you need to specify where each param is expected to be extracted from. As you have things now Jersey/NetBeans is assuming a form post and extracting the data sent as a FormParm. Annotate each of your method augments with where you want the data from (PathParam, FormParam, CookieParam etc)
  • michelson
    michelson over 12 years
    @Klaasvaak - I added a bunch of examples using the various annotations
  • Klaasvaak
    Klaasvaak over 12 years
    Thank you so much for this! This was really what I was looking for. You made my day ;) I am using the FormParams now and it works!
  • Bhavesh
    Bhavesh about 12 years
    Should we pass parameters using @HeaderParam though it is not advised? What are the adverse effect of it?
  • David Frank
    David Frank about 10 years
    Is it possible to access a json like {name: "Mr Blabla", age: 22} as public void create(String name, Integer age) {} ?
  • Kevin Meredith
    Kevin Meredith almost 10 years
    For the excellent poster's MyJaxBean, the fields don't have to be public. Check this answer for what's required to enable Jackson serialization - stackoverflow.com/a/8395924/409976.
  • Biscuit128
    Biscuit128 over 9 years
    perfect - thank you so much for this answer - would also like to agree with the above - i set my fields to private and it works fine
  • PrecisionPete
    PrecisionPete about 9 years
    Seems to work pretty well substituting a Map for the Bean... A bit less coding work.
  • vsingh
    vsingh almost 9 years
    This is the correct link for doc jersey.java.net/documentation/1.19/json.html
  • Constantino Cronemberger
    Constantino Cronemberger over 8 years
    I would remove @Xml* annotations from MyJaxBean because as mentioned in another comment they are not really necessary, so they are creating confusion.
  • Marcus Junius Brutus
    Marcus Junius Brutus almost 8 years
    I have asked a question (stackoverflow.com/q/38573530/274677) on whether it is an anti-pattern to combine more than one methods of passing / accessing parameters.
  • DanielCuadra
    DanielCuadra almost 7 years
    A single answer presented all common options. AWESOME!
  • Alkanshel
    Alkanshel almost 7 years
    Why do the post contents have to be in json? This creates assymetry with other calls, where the same params are instead received in the path rather than in a json blob. I was hoping the http path parameters approach you mentioned would fix this, but I only get 404s when trying it that way. too bad