JAX-RS发布多个对象


75

我有办法

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

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

Answers:


67

答案是否定的

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

现在,当您在请求中收到JSON对象时,您将在request正文中收到它 。该请求可能有几个机构?一个,只有一个。因此,您只能接收一个JSON对象。


2
@Scholle我想知道您如何确定我的答案是关于后续请求的?
Tarlog

嗯,一个List <SomeEntity>有多个对象,不是吗?
Stijn de Witt

@StijndeWitt数组可以包含多个对象。这是数组的目的,对吗?JAX-RS API无法接收多个对象。因此,正如tine2k在他的回答中指出的那样:您可以具有包含多个对象的容器对象。
Tarlog

85

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

3
最后一个(使用QueryParam)无效。错误-“服务器拒绝了此请求,因为请求实体的格式不受请求方法的请求资源所支持(不受支持的媒体类型)”
Sanjay Kumar 2014年

@ tine2k为我List<Object> objects is not working在这里我发布我的问题链接
JustStartedProgramming

1
对于Java新手:不要使用google ObjectOne,它不是特殊的对象类,而是您必须用自己的类名替换的虚拟名称。
Skippy le Grand Gourou

36

如果我们看一下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起作用。以下是Jersey团队的Michal Gadjos(已在此处找到并带说明)已实施的一项。

@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。这使我们可以在请求中发送任意部分。由于每个请求只能有一个实体主体,因此要进行多部分处理,因为它允许将不同部分(具有自己的内容类型)作为实体主体的一部分。

这是使用Jersey的示例(请参见此处的官方文档)

相依性

<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

结果完全符合我们的预期。如果您查看resource方法,我们所要做的就是返回foo.foo + "; " + bar.bar从两个JSON对象收集的string 。

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

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

对于Javascript客户端,我看到的大多数示例(例如其中一些示例都将设置Content-Type为undefined / false(使用FormData),让浏览器对其进行处理。但这对我们不起作用,因为浏览器不会设置Content-Type每个部分的默认类型text/plain

我确定那里有可以设置每个部分类型的库,但是为了向您展示如何手动完成,我将发布一个示例(从此处获得一些帮助。我将使用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,并将stringizedfoobarobject连接到每个部分。如果您不熟悉多部分格式,请参见此处以获取更多信息。另一个有趣的部分是标题。我们将其设置为multipart/form-data

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

在此处输入图片说明

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

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


8

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

TransferObject {
    ObjectOne objectOne;
    ObjectTwo objectTwo;

    //getters/setters
}

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

4

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

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


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

1

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

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

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.