4

For an ordering process in my REST service I have to send a list of "articles" from client to server. These article objects are of a self-made entity type. I already found out that sending a list of STRING or INTEGER objects does work, sending it via @FormParam.

But as soon as I try to send a list of my own objects (even only ONE object), I always get a HTTP 400 error "Bad Request".

I tried excatly the same code like below (only the parameters of form.add() and the parameters of the server method were altered) and postet Strings, Integers and lists of Strings successfully. It only makes problems sending own object types.
Logging told me that the server method isn't reached. The process is broken somewhere before.

I also tried to get the request by using a proxy (Apache JMeter). Here it says that the parameter kunde contains the value entities.Kunde%40af8358. So I guess the object is not serialized thoroughly (or at all). But sending this kind of object from server to client in a response works fine - here the XML-serialization is no problem.

What might be the reason? Is it maybe NOT possible to send own types through POST?
(PS: The type Kundein my example is serializable and annotated with @XmlRootElement.)

Thank you in advance for your help!
Jana

Note: I'm using the SAP Netweaver AS. But until now it behaved like every other Java AS, so I don't think this will be the reason. Every other REST operation does work, even POST without own entities.

Addition: I'm using the JERSEY library.


My Code on server side:

@Path("/test")
    @POST
    @Produces(MediaType.TEXT_XML)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)    
    public String test(
        @FormParam("kunde") Kunde kunde) {    

        return "The name of the customer is: "
           +kunde.getVorname()+" "+kunde.getNachname();        
    }

My code on client side (the method is in a Session Bean):

public String  test() {

    Kunde kunde = new Kunde();
    kunde.setNachname("Müller");
    kunde.setVorname("Kurt");

    Form form = new Form();
    form.add("kunde", kunde);       

    return service
        .path("test")
        .type(MediaType.APPLICATION_FORM_URLENCODED)
        .accept(MediaType.TEXT_XML)
        .post(String.class, form);
}

where service is built like that:

com.sun.jersey.api.client.Client;
com.sun.jersey.api.client.WebResource;
com.sun.jersey.api.client.config.ClientConfig;
com.sun.jersey.api.client.config.DefaultClientConfig;

ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
service = client.resource(UriBuilder.fromUri("<service-url>").build());
Jana
  • 266
  • 3
  • 5
  • 18
  • Can you inspect the actual HTTP request being sent and post that? – chrylis -cautiouslyoptimistic- Sep 09 '13 at 08:31
  • How do I get the built HTTP request? (never done that) – Jana Sep 09 '13 at 08:32
  • You can try [Wireshark](http://www.wireshark.org/) – chrylis -cautiouslyoptimistic- Sep 09 '13 at 08:35
  • Well, this could be difficult. I'm working on a non-administrator PC in a company. I'm not allowed to download the install file (exe) here (I could do this at home), but I'm not sure whether I can successfully use or even install WireShark here... (Yet I'll give it a try!) Is there any other possibility? – Jana Sep 09 '13 at 08:49
  • What's the type of `service`? – chrylis -cautiouslyoptimistic- Sep 09 '13 at 08:51
  • (And if you can't sniff the wire or run your own proxy server, you could just open up a socket and tell the HTTP client to connect to it. The client would throw an `IOException`, but you'd see the exact contents of the request.) – chrylis -cautiouslyoptimistic- Sep 09 '13 at 08:53
  • Please specify the library; I can't figure out which one (there are a *lot* of libraries with `ClientConfig` interfaces). – chrylis -cautiouslyoptimistic- Sep 09 '13 at 09:00
  • Okay, that's a *very* old version of Jersey. I'm having a little trouble reading the docs, and `Form.add()` is not documented *at all*. Are you saying that it works if you change everything else except you pass in a `String` instead of a `Kunde` at that line? What happens, just for fun, if you change "Müller" to something that wouldn't need encoding? – chrylis -cautiouslyoptimistic- Sep 09 '13 at 09:08
  • Yes, it works fine if I "add" a String instead of a Kunde object. And no, it doesn't make a difference with "Müller" oder "Schmidt"... already tried that. :-/ – Jana Sep 09 '13 at 09:13
  • How is it supposed to transform `Kunde` into an HTML form; is `Kunde` `Serializable`? Does it have some sort of `encode` method? – chrylis -cautiouslyoptimistic- Sep 09 '13 at 09:15
  • The type Kunde is serializable and also annotated with `@XmlRootElement`. But it does not have an `encode` method. – Jana Sep 09 '13 at 09:20
  • Could it help to use a newer jersey version..? – Jana Sep 09 '13 at 09:25
  • I'm afraid I'll have to stop at this point; I'm not familiar enough with the specific tools to be of any more help. – chrylis -cautiouslyoptimistic- Sep 09 '13 at 09:26
  • 1
    Well, I tried to get the request by using a proxy (Apache JMeter). Here it says that the parameter `kunde` has a value of `entities.Kunde%40af8358`. So I guess the object is not serialized thoroughly (or at all). But sending this kind of object from server to client in a response works fine - here the XML-serialization is no problem. – Jana Sep 09 '13 at 10:46
  • Jersey is just calling `toString` on `Kunde`. You need to find out what the appropriate hook is to turn `Kunde` into something suitable for inclusion in a `Form`. – chrylis -cautiouslyoptimistic- Sep 09 '13 at 16:32

1 Answers1

2

Due to the new information that the entity "Kunde" isn't trasmitted correctly I found a
SOLUTION:

I explicitely transformed the entity class into XML/JSON (either way is working) and put that XML/JSON as a String in the form. On the server's side I transformed the XML/JSON String back into the Entity, this worked.
(Seems like it is NOT possible to transmit an object not beeing String or Integer as it is.)

Hope this will help everyone who faces the same problem transmitting objects from client to server via REST.
(Sending a List of XML/JSON-converted objects is still to test. I'll add the result here soon.)

Greetings and thanks to chrylis for his/her comments and hints.
Jana


Here's the code for the solution, but for shortness it's only the new parts.

1) The XML solution:

For changing Entity into XML String on Client's side:

...
OutputStream out = new ByteArrayOutputStream();

JAXBContext context = JAXBContext.newInstance(Kunde.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(kunde, out);
out.close();

Form form = new Form();
form.add("entity", out.toString());
...

For transforming the XML back to an Object on the Server's side:

...
public String test(
    @FormParam("entity") String entityString) {      

    InputStream inputStream = new ByteArrayInputStream(entityString.getBytes());        
    Kunde kunde = JAXB.unmarshal(inputStream, Kunde.class);       

    return "Der Name des Kunden ist: "+kunde.getVorname()+" "+kunde.getNachname();
}


2) The JSON solution:

For changing Entity into JSON String on Client's side:

...
OutputStream out = new ByteArrayOutputStream();

ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(out, kunde);
out.close();

Form form = new Form();
form.add("entity", out.toString());
...

For transforming the JSON String back to an Object on the Server's side:

...
public String test(
    @FormParam("entity") String entityString) {      

    InputStream inputStream = new ByteArrayInputStream(entityString.getBytes());        
    Kunde kunde = new ObjectMapper().read((inputStream, Kunde.class));       

    return "Der Name des Kunden ist: "+kunde.getVorname()+" "+kunde.getNachname();
}

The classes JAXB, JAXBContext, Marshaller etc. are from package javax.xml.bind.*. The class ObjectMapper is from package org.codehaus.jackson.map.*.

PS: Because transmitting plain String now, you also could use @QueryParam. But I wouldn't recomment that, because you'd be transmitting the whole XML as a text String in the URL. Same goes for @PathParam.

I'd recommend JSON, because the format is more slender than the XML format, and being slender is the aim of REST.

Jana
  • 266
  • 3
  • 5
  • 18