JAX-RS发布多个对象

时间:2011-04-05 14:09:37

标签: java rest jax-rs

我有一个方法;

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

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

9 个答案:

答案 0 :(得分:83)

无法使用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)

答案 1 :(得分:60)

答案是

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

现在,当您在请求中收到JSON对象时,您会在请求正文中收到该对象。请求可能有多少个尸体?唯一的一个。所以你只能收到一个JSON对象。

答案 2 :(得分:31)

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

我看到了一些解决方案

  1. 使用application / x-www-form-urlencoded
  2. 使用Multipart
  3. 将它们包装在单个父对象中
  4. 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。您可以看到我使用Content-Type为每个部分明确设置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

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

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

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

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

    我确定那里有库可以设置每个部分的类型,但只是为了向您展示如何手动完成,我将发布一个示例(从{获得一点帮助) {3}}。我将使用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功能。这是我们构建multipart的地方,将每个部分的Content-Type设置为application/json,并将字符串化的foobar对象连接到每个部分。如果您不熟悉多部分格式here。另一个有趣的部分是标题。我们将其设置为multipart/form-data

    以下是结果。在Angular中我只使用结果在HTML中显示$scope.result = response.data。您看到的按钮就是发出请求。您还会在see here for more info

    中看到请求数据

    enter image description here

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

    即可

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

答案 3 :(得分:8)

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

TransferObject {
    ObjectOne objectOne;
    ObjectTwo objectTwo;

    //getters/setters
}

@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(TransferObject object){
//        object.getObejctOne()....
}

答案 4 :(得分:4)

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

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

答案 5 :(得分:2)

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

@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

答案 6 :(得分:1)

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

val rdd = df.map(line => ( line(0), (line(1), line(2)))).groupByKey()
      .mapValues(i => i.map(w => (w._1,w._2).toString))
      .map(i=>Row(i._1,i._2))

答案 7 :(得分:0)

更改@Consumes(MediaType.APPLICATION_JSON) 到@Consumes({MediaType.APPLICATION_FORM_URLENCODED}) 然后,您可以将多个对象放入体内

答案 8 :(得分:0)

我的解决方案是为 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));
}