【问题标题】:JAX-RS Post multiple objectsJAX-RS 发布多个对象
【发布时间】:2011-07-30 00:30:20
【问题描述】:

我有方法;

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

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

【问题讨论】:

    标签: java rest jax-rs


    【解决方案1】:

    不能使用 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)
    

    【讨论】:

    • 最后一个(使用 QueryParam)不起作用。错误 - “服务器拒绝此请求,因为请求实体的格式不受所请求方法的请求资源支持(不支持的媒体类型)”
    • @tine2k 为我List&lt;Object&gt; objects is not working 在这里我发布我的问题link
    • 致Java新手:不要googleObjectOne,它不是一个特殊的对象类,而是一个你必须用你自己的类名替换的虚拟名称。
    【解决方案2】:

    答案是

    原因很简单:这与您可以在方法中接收的参数有关。它们必须与请求相关。正确的?因此它们必须是标头或 cookie 或查询参数或矩阵参数或路径参数或 请求正文。 (只是为了讲述完整的故事,还有其他类型的参数称为上下文)。

    现在,当您在请求中收到 JSON 对象时,您会在 请求正文 中收到它。请求可能有多少个主体?一个也只有一个。所以你只能接收一个 JSON 对象。

    【讨论】:

    • @Scholle 我想知道你是如何决定我的回答是关于后续请求的?
    • 嗯,一个 List多个 个对象,不是吗?
    • @StijndeWitt 数组可以包含多个对象。这就是数组的目的吧? JAX-RS API 不能接收多个对象。因此,正如 tine2k 在他的回答中所说:您可以拥有包含多个对象的容器对象。
    • 答案“否”是错误的。 “请求可能有多少具尸体?”在“multipart/form-data”中,可以有更多的主体。
    【解决方案3】:

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

    我看到了一些解决方案

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

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

    另一种选择是只使用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 实施的一个(找到here with 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) {
            // 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。使用多部分

    没有人提到的一种解决方案是使用multipart。这允许我们在请求中发送任意部分。由于每个请求只能有一个实体主体,因此 multipart 是一种解决方法,因为它允许将不同的部分(具有自己的内容类型)作为实体主体的一部分。

    这里是一个使用 Jersey 的例子(见官方文档here

    依赖

    <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,这是上述工作所必需的。多部分提供者将根据每个部分的类型查找消息正文阅读器。如果它没有设置为application/json 或类型,FooBar 有一个阅读器,这将失败。我们将在这里使用 JSON。没有额外的配置,但有一个可用的阅读器。我会用杰克逊。使用以下依赖项,不需要其他配置,因为提供程序将通过类路径扫描发现。

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

    现在是测试。我将使用cURL。你可以看到我用type为每个部分明确设置了Content-Type-F 表示不同的部分。 (请参阅帖子的最底部以了解请求正文的实际外观。)

    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

    结果完全符合我们的预期。如果你看一下资源方法,我们所做的就是返回这个字符串foo.foo + "; " + bar.bar,从两个 JSON 对象中收集。

    您可以在下面的链接中看到一些使用不同 JAX-RS 客户端的示例。您还将从那些不同的 JAX-RS 实现中看到一些服务器端示例。每个链接的某处都应该有一个指向该实现的官方文档的链接

    还有其他 JAX-RS 实现,但您需要自己查找文档。以上三个是我仅有的经验。

    就 Javascript 客户端而言,我看到的大多数示例(例如 some of these 涉及将 Content-Type 设置为 undefined/false(使用 FormData),让浏览器处理它。但这不适用于我们,因为浏览器不会为每个部分设置Content-Type。默认类型是text/plain

    我确信那里有允许为每个部分设置类型的库,但只是为了向您展示如何手动完成,我将发布一个示例(从 here 获得一点帮助。我'将使用 Angular,但构建实体主体的繁重工作将是普通的旧 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 函数。这是我们构建多部分的地方,将每个部分的Content-Type 设置为application/json,并将字符串化的foobar 对象连接到每个部分。如果您不熟悉多部分格式see here for more info。另一个有趣的部分是标题。我们将其设置为multipart/form-data

    下面是结果。在 Angular 中,我只是使用$scope.result = response.data 将结果显示在 HTML 中。您看到的按钮只是为了提出请求。您还将在firebug中看到请求数据

    3。只需将它们包装在一个父对象中

    正如其他人已经提到的,这个选项应该是不言自明的。

    【讨论】:

      【解决方案4】:

      在这种情况下通常采用下一种方法:

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

      【讨论】:

      • 这个答案很好,但是问题是 JAX-RS,并且提供的多部分解决方案在泽西岛。我花了一些时间将其转换为 JAX-RS,接缝更加简单。
      【解决方案5】:

      您不能将两个单独的对象放在一个单独的 POST 调用中,如 Tarlog 所述。

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

      【讨论】:

        【解决方案6】:

        我也遇到过这些问题。也许这会有所帮助。

        @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
        

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

            更改@Consumes(MediaType.APPLICATION_JSON) 到@Consumes({MediaType.APPLICATION_FORM_URLENCODED}) 然后你可以将多个对象放入body中

            【讨论】:

              【解决方案9】:

              我的解决方案是为 CXF 写的,看起来很简单。

              import org.apache.cxf.jaxrs.ext.multipart.Multipart;
              
              @POST
              @Path("paramTest")
              @Consumes(MediaType.MULTIPART_FORM_DATA)
              public GenericResult paramTest(
                  @Multipart(value = "myData", type=MediaType.APPLICATION_JSON)
                      ObjectOne myData,
                  @Multipart(value = "infoList", type=MediaType.APPLICATION_JSON)
                      ObjectTwo[] infoList);
              

              带有 io.restassurred 的测试代码:

              @Test
              public void paramTest()
              {
                  String payload1 = "" +
                      "{ \"name\": \"someName\", \"branch\": \"testBranch\" }";
                  String payload2 =
                      " [ { \"name\": \"cn\", \"status\": \"ts\" }," +
                          "{ \"name\": \"cn2\", \"status\": \"ts2\" } ] ]";
                  RestAssured.
                  given().
                  contentType("multipart/form-data").
                      multiPart("myData", payload1, "application/json").
                      multiPart("infoList", payload2, "application/json").
                  post(String.format("%s/paramTest", API_PATH)).
                  then().
                  statusCode(HttpStatus.SC_OK).
                  contentType(ContentType.JSON).
                  body("success", Matchers.equalTo(true));
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-06-30
                • 2017-11-09
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多