JAX-RS разместить несколько объектов



У меня есть способ;



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


теперь я знаю, что могу опубликовать один объект в формате json, просто поместив его в тело.
Но можно ли сделать несколько объектов? Если да, то как?

615   7  

7 ответов:

ответ нет.

причина проста: это о параметрах, которые вы можете получить в методе. Они должны быть связаны с запросом. Верно? Поэтому они должны быть либо заголовками, либо куки, либо параметрами запроса, либо параметрами матрицы, либо параметрами пути, либо запрос тела. (Просто чтобы рассказать полную историю, есть дополнительные типы параметров, называемые контекстом).

теперь, когда вы получаете объект JSON в своем запросе, вы получаете его в а запрос тела. Сколько органов может быть в запросе? Один и только один. Таким образом, вы можете получить только один объект 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 parameters:

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

если мы посмотрим на то, что OP пытается сделать, он/она пытается опубликовать два (возможно, не связанных) объекта JSON. Сначала любое решение попробовать и отправить одну часть как тело, а одну часть как какой-то другой парам, ИМО, являются ужасно решений. Данные по столба должны пойти в тело. Неправильно делать что-то только потому, что это работает. Некоторые обходные пути могут нарушать основные принципы отдыха.

я вижу несколько решений

  1. использовать application / x-www-form-urlencoded
  2. Использовать Multipart
  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 чтобы заставить это работать. Ниже находится один, который был реализован Михал неверные команды Джерси (нашли здесь с объяснением).

@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. Это позволяет нам отправлять произвольные части в запросе. Поскольку каждый запрос может иметь только одно тело, составные это обойти, так как он позволяет иметь разные части (с собственные типы контента), как часть тела сущности.

вот пример использования Джерси (см. официальный документ здесь)

зависимость

<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 каждой части тела, которая необходима для вышеуказанного для работы. Поставщик multipart будет искать тело сообщения читатель, основанный на типе каждой части. Если он не установлен в application/json или типа Foo или Bar есть читатель для, это не удастся. Мы будем использовать JSON здесь. Там нет никакой дополнительной конфигурации, но иметь читателя доступны. Я использую Джексона. С приведенной ниже зависимостью никакая другая конфигурация не должна требоваться, поскольку поставщик будет обнаружен путем сканирования classpath.

<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.

вы можете увидеть некоторые примеры используя некоторые различные клиенты 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>

интересная часть

в таких случаях обычно применяется следующий подход:

TransferObject {
    ObjectOne objectOne;
    ObjectTwo objectTwo;

    //getters/setters
}

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

вы не можете поместить два отдельных объекта в один вызов POST, как описано в Tarlog.

в любом случае вы можете создать третий объект контейнера, который содержит первые два объекта и передать его в вызове 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 protected]",                 "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);
  }
}

Comments

    Ничего не найдено.