Как передать несколько аргументов в REST api [duplicate]

Вот рекурсивная функция для установки innerHTML элемента, который я использую на нашем сервере объявлений:

// o: container to set the innerHTML
// html: html text to set.
// clear: if true, the container is cleared first (children removed)
function setHTML(o, html, clear) {
    if (clear) o.innerHTML = "";

    // Generate a parseable object with the html:
    var dv = document.createElement("div");
    dv.innerHTML = html;

    // Handle edge case where innerHTML contains no tags, just text:
    if (dv.children.length===0){ o.innerHTML = html; return; }

    for (var i = 0; i < dv.children.length; i++) {
        var c = dv.children[i];

        // n: new node with the same type as c
        var n = document.createElement(c.nodeName);

        // copy all attributes from c to n
        for (var j = 0; j < c.attributes.length; j++)
            n.setAttribute(c.attributes[j].nodeName, c.attributes[j].nodeValue);

        // If current node is a leaf, just copy the appropriate property (text or innerHTML)
        if (c.children.length == 0)
        {
            switch (c.nodeName)
            {
                case "SCRIPT":
                    if (c.text) n.text = c.text;
                    break;
                default:
                    if (c.innerHTML) n.innerHTML = c.innerHTML;
                    break;
            }
        }
        // If current node has sub nodes, call itself recursively:
        else setHTML(n, c.innerHTML, false);
        o.appendChild(n);
    }
}

Вы можете увидеть демо здесь .

62
задан Thys 5 April 2011 в 15:09
поделиться

7 ответов

Ответ - нет.

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

Теперь, когда вы получаете объект JSON в своем запросе, вы получаете его в теле запроса. Сколько тел может иметь запрос? Один и единственный. Таким образом, вы можете получить только один объект JSON.

57
ответ дан Tarlog 18 August 2018 в 07:17
поделиться
  • 1
    @Scholle Интересно, как вы решили, что мой ответ о последующих запросах? – Tarlog 11 July 2013 в 12:47
  • 2
    Mmmm a List & lt; SomeEntity & gt; имеет несколько объектов, нет? – Stijn de Witt 21 March 2017 в 21:32
  • 3
    @StijndeWitt Array может содержать несколько объектов. Это цель массива, не так ли? API JAX-RS не может получать несколько объектов. Так как tine2k заявляет в своем ответе: вы можете иметь контейнерный объект, который будет содержать несколько объектов. – Tarlog 24 March 2017 в 19:30

Если мы посмотрим, что пытается сделать OP, он пытается опубликовать два (возможно, несвязанных) объекта JSON. Прежде всего, любое решение попробовать и отправить одну часть в качестве тела, а одна часть как другой параметр IMO - это ужасные решения. Данные POST должны поступать в тело. Неправильно делать что-то только потому, что оно работает. Некоторые работы могут нарушать основные принципы REST.

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

  1. Использовать приложение / x-www-form-urlencoded
  2. Использовать Multipart
  3. Просто обернуть их в одном родительском объекте

1. Использовать приложение / 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

Одно из решений, о котором никто не упомянул, заключается в использовании multipart . Это позволяет нам отправлять произвольные части в запрос. Поскольку каждый запрос может иметь только один объект, многопроцессор - это работа вокруг, поскольку он позволяет иметь разные части (с их собственными типами контента) как часть тела сущности.

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

Зависимость

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

Resource

g32]
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 или тип, для Foo или Bar будет читатель, это не удастся. Мы будем использовать 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

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

Интересной частью является функция createRequest. где мы построим multipart, установив Content-Type каждой части в application/json и объединив стробированные объекты foo и bar к каждой части. Если вы не знакомы с многостраничным см. здесь для получения дополнительной информации . Другая интересная часть - это заголовок. t до multipart/form-data.

Ниже приведен результат. В Angular я использовал результат, чтобы показать в HTML, с $scope.result = response.data. Кнопка, которую вы видите, просто должна была сделать запрос. Вы также увидите данные запроса в firebug

enter image description here [/g15]

3. Просто заверните их в один родительский объект

Этот параметр должен быть понятным, как это уже упоминалось другими.

28
ответ дан Community 18 August 2018 в 07:17
поделиться

Я также столкнулся с этой проблемой. Возможно, это поможет.

@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
0
ответ дан dur 18 August 2018 в 07:17
поделиться

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

В любом случае вы могли бы создать третий контейнерный объект, который содержит первые два объекта и передать тот, который находится внутри вызова POS .

4
ответ дан Giorgio 18 August 2018 в 07:17
поделиться

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

TransferObject {
    ObjectOne objectOne;
    ObjectTwo objectTwo;

    //getters/setters
}

@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
public void test(TransferObject object){
//        object.getObejctOne()....
}
5
ответ дан Oleksii Kyslytsyn 18 August 2018 в 07:17
поделиться

Это можно сделать, если объявленный метод POST принимает массив объектов. Пример, подобный этому

T[] create(@RequestBody T[] objects) {
for( T object : objects ) {
   service.create(object);
  }
}
0
ответ дан Petter Friberg 18 August 2018 в 07:17
поделиться

Вы не можете использовать свой метод, как это, как указано в 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)
76
ответ дан tine2k 18 August 2018 в 07:17
поделиться
  • 1
    Последний (с QueryParam) не работает. Ошибка - «Сервер отказал этот запрос, потому что объект запроса находится в формате, не поддерживаемом запрошенным ресурсом для запрошенного метода (Unsupported Media Type) & quot; – Sanjay Kumar 28 March 2014 в 21:31
  • 2
    @ tine2k для меня List<Object> objects is not working здесь я отправляю свою проблему link – JustStartedProgramming 19 March 2018 в 16:36
Другие вопросы по тегам:

Похожие вопросы: