JAX-RS with Jersey: Passing form parameters to PUT method for updating a Resource

30,532

Solution 1

In addition to annotating your method with @Consumes(MediaType.APPLICATION_FORM_URLENCODED), you must send application/x-www-form-urlencodedas a content-type. Did you do it?

Edited: You can use FormParams only with POST:

SRV.4.1.1 When Parameters Are Available The following are the conditions that must be met before post form data will be populated to the parameter set:

  1. The request is an HTTP or HTTPS request.
  2. The HTTP method is POST.
  3. The content type is application/x-www-form-urlencoded.
  4. The servlet has made an initial call of any of the getParameter family of methods on the request object. If the conditions are not met and the post form data is not included in the parameter set, the post data must still be available to the servlet via the request object’s input stream. If the conditions are met, post form data will no longer be available for reading directly from the request object’s input stream.

Solution 2

it is not true that PUT does not accept FormParameters. I use @FormParam with PUT successfully. Maybe it's the @PathParam. I have not tried that.

There seems to be no need for special annotations either. Here is a working class in my application:

package ch.sertal.vision.server.services.objectstore;

import ch.sertal.vision.dao.FormDao;
import ch.sertal.vision.dao.PrepopContentDao;
import ch.sertal.vision.model.Form;
import ch.sertal.vision.model.PrepopContent;
import ch.sertal.vision.model.User;
import ch.sertal.vision.model.exceptions.NotLoggedinException;
import ch.sertal.vision.server.services.AbstractSertalService;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Set;

@Path( "jsonrest/prepop" )
public class PrepopContentStore extends AbstractSertalService {

   @GET
   @Path( "list" )
   @Produces( "application/json" )
   public Response getPrepopEntries( @QueryParam( "formId" ) Long formId ) {
      User user;
      try {
         user = getUser();
      } catch ( NotLoggedinException e ) {
         return Response.status( Response.Status.UNAUTHORIZED ).build();
      }

      PrepopContentDao prepopContentDao = new PrepopContentDao( db );
      List<PrepopContent> contentList = prepopContentDao.list( user, formId );

      JSONArray jsPrepops = new JSONArray();
      for ( PrepopContent content : contentList ) {
         jsPrepops.add( prepopContentDao.getJsContent( content, user ) );
      }

      return Response.ok( jsPrepops.toJSONString(), MediaType.APPLICATION_JSON_TYPE ).build();
   }

   @PUT
   @Produces( "application/json" )
   public Response newPrepopContent( @FormParam( "formId" ) Long formId ) {
      User user;
      try {
         user = getUser();
      } catch ( NotLoggedinException e ) {
         return Response.status( Response.Status.UNAUTHORIZED ).build();
      }

      Form form = (new FormDao( db)).get( formId );

      PrepopContent content = new PrepopContent( form, "" );
      content.setTenant( user.getMainTenant() );
      PrepopContentDao prepopContentDao = new PrepopContentDao( db );
      content = prepopContentDao.save( content );

      return Response.ok( prepopContentDao.getJsContent( content, user ).toJSONString(),
                  MediaType.APPLICATION_JSON_TYPE ).build();
   }

}

It is called from jQuery like so:

    $.ajax( {
       url:"${pageContext.request.contextPath}/services/jsonrest/prepop",
       type:"PUT",
       async:true,
       data: [{name: "formId", value: that.formId}],
       success:function ( prepopContent ) {
          listContainer.append( "<div>" + template( prepopContent ) + "</div>" );
       }
    } );

the ${pageContext.request.contextPath} is from JSP

Solution 3

Alternatively you can use an approach as indicated in Overwrite HTTP method with JAX-RS :

You basically would add a hidden parameter / HTTP header and then POST the form. In your servlet, you prepend a Filter that checks for a certain header / hidden form element and changes the POST into PUT request. This is forwarded to your ressource and consumed correctly when annotated with PUT.

Share:
30,532
Amit Patel
Author by

Amit Patel

A freelance developer based in Ahmedabad, India. Passionate about web technologies and following best practices. Focusing on Ruby on Rails. I still love vanilla JS and jQuery. LinkedIn: https://www.linkedin.com/in/amitsavani Email: amit [DOT] savani [AT] gmail

Updated on November 16, 2020

Comments

  • Amit Patel
    Amit Patel over 3 years

    I have to update a Person record having firstName and lastName. User should be able to change it from html form and on submit it should be updated.

    Here is my code.

        @PUT
        @Path("/{userId}")
        public Response updatingResource(@FormParam("firstName") String firstName, @FormParam("lastName ") String lastName , @PathParam("userId") String userId){
            System.out.println(firstName);
            System.out.println(lastName);
            return Response.ok().build();
        }
    

    the SOP statements prints null. I have been using Mozilla Firefox's Poster plugin to send PUT request.

    I also tried by annotating it with @Consumes(MediaType.APPLICATION_FORM_URLENCODED) but still it is printing null for each values.

    How to write and call PUT method that receives these three values. I stumble around lot and found people were asking to use JSON or XML. How can I consume JSON? I would be very greatfull if someone help me to write REST method to update a resource


    If I send PUT request using Firefox's RESTClient and Google's rest-client I am able to get the form parameters. Both this tool has something like body section where I placed firstName=Amit&lastName=Patel. Also I added header Content-Type as application/x-www-form-urlencoded.I think Firefox's Poster is buggy. Can anyone suggest me is there any other way I should validate the code or I can trust on first two REST clients?

  • Amit Patel
    Amit Patel almost 13 years
    Yes, I am specifying content type as {application/x-www-form-urlencoded} while sending request
  • Tarlog
    Tarlog almost 13 years
    Oh, didn't pay attention that you use PUT: you must use POST with form parameters.
  • Amit Patel
    Amit Patel almost 13 years
    If you want to change the resource you should use PUT. POST method is used when you want to create resource. Here I want to change Person details so used PUT. Should I use POST instead?
  • Tarlog
    Tarlog almost 13 years
    Yes, I know, but form parameters will work only with POST. See the edited answer.
  • Amit Patel
    Amit Patel almost 13 years
    With this approach I am able to land POST request with header "X-HTTP-Method-Override" set to PUT but still form parameters are null.
  • Dirk
    Dirk almost 13 years
    Hm, hard to tell, after hooking the Filter in your servlet, it should have changed the request as needed. Any errors you get?
  • Amit Patel
    Amit Patel almost 13 years
    Please check my answer below. I think I was using buggy tool to send REST PUT request
  • Dirk
    Dirk almost 13 years
    With the above mentioned method, you should be able to POST (which all browsers can do) and either add the header so that the Jersey filter kicks in or add a hidden form parameter (in case of the Spring filter mentioned in the post). So the request will just be transformed internally - after reaching the server - to the required PUT request.
  • alan-p
    alan-p over 11 years
    You may also get nulls If you set the "id" tag instead of the "name" tag in your html form field definitions. So <input type="text" id="username" /> will return null but <input type="text" name="username" /> will return the proper result.