@Consumes(MediaType.APPLICATION_FORM_URLENCODED) FormParameter is null

23,223

Try using a @Consumes(MediaType.APPLICATION_XML) (also set it when you send the request) and then remove the @FormParam("payload") annotation. When you send the payload in your client, make sure it is XML encoded (without payload=).

Otherwise, if you want to stick with your schema (mixing MediaType.APPLICATION_FORM_URLENCODED with XML) you will have to decode manually the encoded XML String. What I mean is the following:

@Path("/webhookservice")
public class Webhook {

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response readData(@FormParam("payload") String payload, @Context HttpServletRequest req) {
        JAXBContext jaxbContext = JAXBContext.newInstance(Payload.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

        StringReader reader = new StringReader(payload,);
        Payload payload = (Payload) unmarshaller.unmarshal(reader);

        //...
    }
}

Why you must work with a String instead of a Payload parameter? Because noone tells Jersey/JAX-RS how to decode that String: it may be a JSON String, or an XML String.

Share:
23,223
Admin
Author by

Admin

Updated on August 13, 2020

Comments

  • Admin
    Admin over 3 years

    I've created a Jersey webservice that looks like this:

    @Path("/webhookservice")
    public class Webhook {
    
        @POST
        @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
        public Response readData(@FormParam("payload") Payload payload, @Context HttpServletRequest req) {
    
            // Gets client IP address
            String ipAddress = (req.getRemoteHost().equals("") || req.getRemoteHost() == null) ? "UNKNOWN" : req.getRemoteHost();
    
            // Persist data to DB
            Persist.saveToDb(payload.getId(), Integer.toString(payload.getTimestamp()),
                    payload.getStatus(), payload.getAuth_code(),
                    payload.getTotal_amount(), payload.getRequired_amount(),
                    ipAddress);
    
            // Repsond with a HTTP 200 OK
            Response response = Response.status(200).build();
            return response;
    
        }
    
        @GET
        @Produces("text/plain")
        public String testService() {
            return "Service is up and working!";
        }
    
    }
    

    The Payload class looks like this:

    @XmlRootElement
    public class Payload {
    
        private String id;
        private int timestamp;    
        private String status;    
        private String auth_code;    
        private int total_amount;    
        private int required_amount;
    
        // Getters
        @XmlElement(name = "id")
        public String getId() {
            return this.id;
        }
    
        @XmlElement(name = "timestamp")
        public int getTimestamp() {
            return this.timestamp;
        }
    
        @XmlElement(name = "status")
        public String getStatus() {
            return this.status;
        }
    
        @XmlElement(name = "auth_code")
        public String getAuth_code() {
            return this.auth_code;
        }
    
        @XmlElement(name = "total_amount")
        public int getTotal_amount() {
            return this.total_amount;
        }
    
        @XmlElement(name = "required_amount")
        public int getRequired_amount() {
            return this.required_amount;
        }
    
        // Setters
        public void setId(String id) {
            this.id = id;
        }
    
        public void setTimestamp(int timestamp) {
            this.timestamp = timestamp;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public void setAuth_code(String auth_code) {
            this.auth_code = auth_code;
        }
    
        public void setTotal_amount(int total_amount) {
            this.total_amount = total_amount;
        }
    
        public void setRequired_amount(int required_amount) {
            this.required_amount = required_amount;
        }
    
    }
    

    And the request that I'm sending looks like this:

    Content-Type: application/x-www-form-urlencoded Method: POST

    Unencoded body:

    payload={"id":"123123","auth_code":"191331","required_amount":101,"timestamp":1407775713,"status":"completed","total_amount":101}
    

    Encoded body:

    payload%3D%7B%22id%22%3A%22123123%22%2C%22auth_code%22%3A%22191331%22%2C%22required_amount%22%3A101%2C%22timestamp%22%3A1407775713%2C%22status%22%3A%22completed%22%2C%22total_amount%22%3A101%7D
    

    But when I send the request off the 'payload' object is null in my 'readData()' function in the 'Webhook' class... Can anybody point me in the right direction?

  • Admin
    Admin over 9 years
    I'm writing a webhook that received data from an outside company and they're not willing to change they request body type to application/xml. It has to stay application/x-www-form-urlencoded
  • V G
    V G over 9 years
    No problems. If you must stick with that, I added more details in my answer. But I consider those guys do not really know what they do: the payload is probably the only thing that is sent with such a request, so it is not needed to put in a FormParam.
  • V G
    V G over 9 years
    @Relborg did you try my solution?
  • Admin
    Admin over 9 years
    Ah yes! Thanks :-) I did use another JSON parsing library though. But yes it worked, thank you!