If we look at what the OP is trying to do, he / she is trying to publish two (possibly unrelated) JSON objects. First, any solution to try to send one part as a body, and one part as some other parameter, IMO, are terrible decisions. POST data must go in the body. It is not right to do something just because it works. Some workarounds may violate the basic principles of REST.
I see some solutions
- Use the app / x-www -F orm-urlencoded
- Use Multipart
- Just wrap them in one parent
1. Use application / x-www -F orm-urlencoded
Another option is to simply use application/x-www-Form-urlencoded . In fact, we can have JSON values. For exam
curl -v http://localhost:8080/api/model \ -d 'one={"modelOne":"helloone"}' \ -d 'two={"modelTwo":"hellotwo"}' public class ModelOne { public String modelOne; } public class ModelTwo { public String modelTwo; } @Path("model") public class ModelResource { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String post(@FormParam("one") ModelOne modelOne, @FormParam("two") ModelTwo modelTwo) { return modelOne.modelOne + ":" + modelTwo.modelTwo; } }
The only thing we need for this to work is ParamConverterProvider for this to work. Below is an example that was implemented by Michal Gajos from the Jersey team (located here with an explanation ).
@Provider public class JacksonJsonParamConverterProvider implements ParamConverterProvider { @Context private Providers providers; @Override public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType, final Annotation[] annotations) {
If you are not scanning resources and providers, simply register this provider, and the above example should work.
2. Use Multipart
One solution that no one has mentioned is to use multipart . This allows us to send arbitrary parts in the request. Since each request can have only one object body, the workaround is to bypass, since it allows you to have different parts (with its own types of content) as part of the object body.
Here is an example of using a jersey (see white paper here )
dependence
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-multipart</artifactId> <version>${jersey-2.x.version}</version> </dependency>
Register MultipartFeature
import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; @ApplicationPath("/api") public class JerseyApplication extends ResourceConfig { public JerseyApplication() { packages("stackoverflow.jersey"); register(MultiPartFeature.class); } }
Resource class
import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glassfish.jersey.media.multipart.FormDataParam; @Path("foobar") public class MultipartResource { @POST @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postFooBar(@FormDataParam("foo") Foo foo, @FormDataParam("bar") Bar bar) { String response = foo.foo + "; " + bar.bar; return Response.ok(response).build(); } public static class Foo { public String foo; } public static class Bar { public String bar; } }
Now, the complexity of some clients is that there is no way to set the Content-Type each part of the body, which is necessary for the above. The multiuser provider will search for a message body reader, depending on the type of each part. If application/json not set for it or the type for which Foo or Bar has a reader, this will fail. We will use JSON here. There is no additional configuration besides having a reader. I will use Jackson. Based on the dependency below, no other configuration is required, as the provider will be discovered by scanning the classpath.
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>${jersey-2.x.version}</version> </dependency>
Now the test. I will use cURL . You can see that I explicitly set the Content-Type for each part with type . -F means the other part. (See at the bottom of the post for an idea of ββwhat the request body actually looks like.)
curl -v -X POST\-H "Content-Type:multipart/form-data"\-F "bar={\"bar\":\"BarBar\"};type=application/json"\-F "foo={\"foo\":\"FooFoo\"};type=application/json"\http://localhost:8080/api/foobar
Result: FooFoo; BarBar FooFoo; BarBar FooFoo; BarBar FooFoo; BarBar
The result is exactly as we expected. If you look at the resource method, all we do is return this line foo.foo + "; " + bar.bar , assembled from two JSON objects.
You can see some examples of using various JAX-RS clients from the links below. You will also see some server side example from these various JAX-RS implementations. Each link should have somewhere a link to the official documentation for this implementation.
- Jersey example
- Restysi example
- CXF example
There are other implementations of JAX-RS, but you will need to find documentation for them yourself. The above three are the only ones with which I have experience.
As for Javascript clients, most of the examples that I see (for example, some of them include setting the Content-Type to undefined / false (using FormData ), allowing the browser to handle it. But this will not work for us, as the Browser will not set a Content-Type for each part, and the default type is text/plain .
I am sure that there are libraries that allow you to set the type for each part, but just to show you how to do it manually, I will publish an example (I will help a bit here . I will use Angular, but the main work is to create an entity body will be plain old javascript.
<!DOCTYPE html> <html ng-app="multipartApp"> <head> <script src="js/libs/angular.js/angular.js"></script> <script> angular.module("multipartApp", []) .controller("defaultCtrl", function($scope, $http) { $scope.sendData = function() { var foo = JSON.stringify({foo: "FooFoo"}); var bar = JSON.stringify({bar: "BarBar"}); var boundary = Math.random().toString().substr(2); var header = "multipart/form-data; charset=utf-8; boundary=" + boundary; $http({ url: "/api/foobar", headers: { "Content-Type": header }, data: createRequest(foo, bar, boundary), method: "POST" }).then(function(response) { $scope.result = response.data; }); }; function createRequest(foo, bar, boundary) { var multipart = ""; multipart += "--" + boundary + "\r\nContent-Disposition: form-data; name=foo" + "\r\nContent-type: application/json" + "\r\n\r\n" + foo + "\r\n"; multipart += "--" + boundary + "\r\nContent-Disposition: form-data; name=bar" + "\r\nContent-type: application/json" + "\r\n\r\n" + bar + "\r\n"; multipart += "--" + boundary + "--\r\n"; return multipart; } }); </script> </head> <body> <div ng-controller="defaultCtrl"> <button ng-click="sendData()">Send</button> <p>{{result}}</p> </div> </body> </html>
The interesting part is the createRequest function. Here we build the component by setting the Content-Type each part to application/json and concatenating the string objects foo and bar for each part. If you are not familiar with the multi-part format, see here for more information . Another interesting part is the headline. We set it to multipart/form-data .
Below is the result. In Angular, I just used the result to display in HTML, with $scope.result = response.data . The button you see was just for making a request. You will also see request data in firebug

3. Just wrap them in one parent
This option should be understood, as others have already mentioned.