JAX-RS发布多个对象

我有一个方法;

@POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(ObjectOne objectOne, ObjectTwo objectTwo) 

现在我知道我可以发布一个JSON格式的对象,只需将其放入正文中即可。 但是有可能做多个对象吗? 如果是这样,怎么样?

答案是否定的

原因很简单:关于您可以在方法中接收的参数。 他们必须与请求有关。 对? 所以他们必须是头或cookies或查询参数或matrix参数或path参数或请求正文 。 (只是告诉完整的故事还有其他types的参数称为上下文)。

现在,当您在请求中收到JSON对象时,您会在请求正文中接收到该对象。 请求可能有多less个机构? 一个也是唯一一个。 所以你只能收到一个JSON对象。

不能像Tarlog所说的那样使用你的方法。

不过,你可以这样做:

 @POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(List<ObjectOne> objects) 

或这个:

 @POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(BeanWithObjectOneAndObjectTwo containerObject) 

而且,你总是可以将你的方法和GET参数结合起来:

 @POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(List<ObjectOne> objects, @QueryParam("objectTwoId") long objectTwoId) 

如果我们看看OP在做什么,他/她正试图发布两个(可能不相关的)JSON对象。 首先,任何解决方法都是尝试发送一个部分作为主体,另一部分作为其他参数IMO的一部分,这是可怕的解决scheme。 POST数据应该放在主体中。 仅仅因为它的作用而做某件事是不对的。 有些解决方法可能违反了基本的REST原则。

我看到一些解决scheme

  1. 使用application / x-www-form-urlencoded
  2. 使用多部分
  3. 只需将它们包装在单个父对象中

1.使用application / x-www-form-urlencoded

另一个select是只使用application/x-www-form-urlencoded 。 我们实际上可以有JSON值。 为了例证

 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; } } 

我们需要让这个工作的一件事是一个ParamConverterProvider得到这个工作。 下面是泽西队的Michal Gadjos执行的一个(在这里可以find解释 )。

 @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) { // Check whether we can convert the given type with Jackson. final MessageBodyReader<T> mbr = providers.getMessageBodyReader(rawType, genericType, annotations, MediaType.APPLICATION_JSON_TYPE); if (mbr == null || !mbr.isReadable(rawType, genericType, annotations, MediaType.APPLICATION_JSON_TYPE)) { return null; } // Obtain custom ObjectMapper for special handling. final ContextResolver<ObjectMapper> contextResolver = providers .getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE); final ObjectMapper mapper = contextResolver != null ? contextResolver.getContext(rawType) : new ObjectMapper(); // Create ParamConverter. return new ParamConverter<T>() { @Override public T fromString(final String value) { try { return mapper.reader(rawType).readValue(value); } catch (IOException e) { throw new ProcessingException(e); } } @Override public String toString(final T value) { try { return mapper.writer().writeValueAsString(value); } catch (JsonProcessingException e) { throw new ProcessingException(e); } } }; } } 

如果你不扫描资源和提供者,只需注册这个提供者,上面的例子应该可以工作。

2.使用多部分

没有人提到的一个解决scheme是使用多部分 。 这允许我们发送请求中的任意部分。 由于每个请求只能有一个实体主体,所以multipart是工作的一部分,因为它允许将不同部分(具有自己的内容types)作为实体主体的一部分。

这里是一个使用泽西岛的例子(见官方文档在这里 )

依赖

 <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-multipart</artifactId> <version>${jersey-2.x.version}</version> </dependency> 

注册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); } } 

资源类

 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; } } 

现在,有些客户端的棘手部分是,没有办法设置每个正文部分的Content-Type ,这是上述工作所需的。 多部分提供者将根据每个部分的types来查找消息体读取器。 如果它没有设置为application/json或者types, Foo或者Bar有一个阅读器,这将会失败。 我们将在这里使用JSON。 没有额外的configuration,但有一个阅读器可用。 我会用jackson。 通过下面的依赖关系,不需要其他configuration,因为提供者将通过类path扫描被发现。

 <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>${jersey-2.x.version}</version> </dependency> 

现在testing。 我将使用cURL 。 你可以看到我明确地为每个Content-Type的部分设置了Content-Type-F表示不同的部分。 (请参阅post的底部,了解请求主体的外观。)

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
结果: FooFoo; BarBar FooFoo; BarBar

结果完全如我们所料。 如果你看资源方法,我们所做的只是返回这个stringfoo.foo + "; " + bar.bar ,从两个JSON对象中收集。

您可以在下面的链接中看到一些使用不同JAX-RS客户端的示例。 您还将从不同的JAX-RS实现中看到一些服务器端的例子。 每一个环节都应该有一个链接到这个实施的正式文件的链接

  • 泽西的例子
  • Resteasy的例子
  • CXF的例子

还有其他的JAX-RS实现,但你需要自己find它的文档。 以上三个是我有经验的唯一的。

至于Javascript客户端,我看到的大部分例子(例如, 其中一些包括设置Content-Type为undefined / false(使用FormData ),让浏览器处理它,但是这对我们不起作用,因为浏览器不会为每个部分设置Content-Type ,而默认的types是text/plain

我确定有一些库允许为每个部分设置types,但只是为了向您展示如何手动完成,我将发布一个示例(从这里得到一些帮助,我将使用Angular ,但是构build实体的繁琐工作将会是简单的旧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> 

有趣的部分是createRequest函数。 这是我们构build多部分的地方,将每个部分的Content-Type设置为application/json ,并将string化的foobar对象连接到每个部分。 如果你是不熟悉的多部分格式, 请参阅这里获取更多信息 。 另一个有趣的部分是标题。 我们将其设置为multipart/form-data

以下是结果。 在Angular中,我只用了$scope.result = response.data来显示HTML。 你看到的button只是为了提出请求。 您还将在萤火虫中看到请求数据

在这里输入图像描述

把它们包装在一个单独的父对象中

和其他人已经提到的一样,这个选项应该是自我解释的。

下一种方法通常适用于这种情况:

 TransferObject { ObjectOne objectOne; ObjectTwo objectTwo; //getters/setters } @POST @Path("test") @Consumes(MediaType.APPLICATION_JSON) public void test(TransferObject object){ // object.getObejctOne().... } 

您不能像Tarlog所解释的那样在一个POST调用中放置两个单独的对象。

无论如何,您可以创build第三个包含前两个对象的容器对象,并在POS调用中传递该对象。

我也面临这些问题。 也许这会有所帮助。

 @POST @Path("/{par}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Object centralService(@PathParam("par") String operation, Object requestEntity) throws JSONException { ObjectMapper objectMapper=new ObjectMapper(); Cars cars = new Cars(); Seller seller = new Seller(); String someThingElse; HashMap<String, Object> mapper = new HashMap<>(); //Diamond ))) mapper = (HashMap<String, Object>) requestEntity; cars=objectMapper.convertValue(mapper.get("cars"), Cars.class); seller=objectMapper.convertValue(mapper.get("seller"), Seller.class); someThingElse=objectMapper.convertValue(mapper.get("someThingElse"), String.class); System.out.println("Cars Data "+cars.toString()); System.out.println("Sellers Data "+seller.toString()); System.out.println("SomeThingElse "+someThingElse); if (operation.equals("search")) { System.out.println("Searching"); } else if (operation.equals("insertNewData")) { System.out.println("Inserting New Data"); } else if (operation.equals("buyCar")) { System.out.println("Buying new Car"); } JSONObject json=new JSONObject(); json.put("result","Works Fine!!!"); return json.toString(); } *******CARS POJO********@XmlRootElement for Mapping Object to XML or JSON*** @XmlRootElement public class Cars { private int id; private String brand; private String model; private String body_type; private String fuel; private String engine_volume; private String horsepower; private String transmission; private String drive; private String status; private String mileage; private String price; private String description; private String picture; private String fk_seller_oid; } // Setters and Getters Omitted *******SELLER POJO********@XmlRootElement for Mapping Object to XML or JSON*** @XmlRootElement public class Seller { private int id; private String name; private String surname; private String phone; private String email; private String country; private String city; private String paste_date; }//Setters and Getters omitted too *********************FRONT END Looks Like This****************** $(function(){ $('#post').on('click',function(){ console.log('Begins'); $.ajax({ type:'POST', url: '/ENGINE/cars/test', contentType: "application/json; charset=utf-8", dataType: "json", data:complexObject(), success: function(data){ console.log('Sended and returned'+JSON.stringify(data)); }, error: function(err){ console.log('Error'); console.log("AJAX error in request: " + JSON.stringify(err, null, 2)); } }); //-- END of Ajax console.log('Ends POST'); console.log(formToJSON()); }); // -- END of click function POST function complexObject(){ return JSON.stringify({ "cars":{"id":"1234","brand":"Mercedes","model":"S class","body_type":"Sedan","fuel":"Benzoline","engine_volume":"6.5", "horsepower":"1600","transmission":"Automat","drive":"Full PLag","status":"new","mileage":"7.00","price":"15000", "description":"new car and very nice car","picture":"mercedes.jpg","fk_seller_oid":"1234444"}, "seller":{ "id":"234","name":"Djonotan","surname":"Klinton","phone":"+994707702747","email":"email@gmail.com", "country":"Azeribaijan","city":"Baku","paste_date":"20150327"}, "someThingElse":"String type of element" }); } //-- END of Complex Object });// -- END of JQuery - Ajax 

它可以通过声明POST方法来接受数组对象来完成。 像这样的例子

 T[] create(@RequestBody T[] objects) { for( T object : objects ) { service.create(object); } }