Как передать сложные объекты в качестве аргументов службе RESTful? - PullRequest
35 голосов
/ 19 мая 2011

Я успешно настроил быстрый тест для создания «REST-подобного» сервиса, который возвращает объект, сериализованный в JSON, и это было довольно просто и быстро (на основе этой статьи ).

Но хотя возвращать JSON-ified объекты было легко, как персику, я еще не видел примеров, относящихся к входным параметрам, которые не являются примитивами.Как я могу передать сложный объект в качестве аргумента?Я использую Apache CXF, но примеры с использованием других фреймворков, таких как Джексон, тоже приветствуются:)

На стороне клиента, вероятно, будет что-то вроде создания объекта javascript, передачи его в JSON.stringify (complexObj) и передачи этой строкив качестве одного из параметров.

Служба, вероятно, будет выглядеть примерно такВы бы порекомендовали использовать POST в этих случаях, и мне нужно было бы сильно изменить определения функций?

Ответы [ 3 ]

32 голосов
/ 09 июня 2011

Немного покопавшись, я быстро обнаружил, что в основном есть две опции:

Опция 1

Вы передаете «объект-обертку», содержащий все остальные параметры, службе.Возможно, вам потребуется аннотировать этот класс-оболочку аннотациями JAXB, такими как @XmlRootElement, чтобы он работал с провайдером на основе Jettison, но если вы используете Джексона вместо него, в этом нет необходимости.Просто установите тип контента на правильный тип, и будет вызван правильный читатель тела сообщения.Конечно, это будет работать только для служб типа POST (AFAIK).

Пример

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

@Service("myService")
class RestService {

    @POST
    @Produces("application/json")
    @Path("/fooBar")
    public Result fooBar(

          /** 
          * Using "" will inject all form params directly into a ParamsWrapper 
          * @see http://cxf.apache.org/docs/jax-rs-basics.html
          */
          @FormParam("") FooBarParamsWrapper wrapper

        ) throws WebServiceException {
            doSomething(wrapper.foo);
    }
}

class ParamsWrapper {
  double foo, bar;
  MyComplexObject object;
}

Опция 2

Вы можете предоставить специальный формат строки, в который вы упаковываете свои объекты, а затем реализуете конструктор, принимающий строку, static valueOf (String s) или static fromString (Строка s) в классе, который возьмет эту строку и создаст из нее объект.Или совсем аналогично, создайте ParameterHandler, который делает то же самое.

AFAIK, только вторая версия позволит вам вызывать ваши сервисы из браузера, используя JSONP (поскольку JSONP - это трюк, ограниченный GET).Я выбрал этот маршрут, чтобы иметь возможность передавать массивы сложных объектов в URI.

В качестве примера того, как это работает, возьмем следующий класс домена и службу

Пример

@GET
@Path("myService")
public void myService(@QueryParam("a") MyClass [] myVals) {
    //do something
}

class MyClass {
    public int foo;
    public int bar;

   /** Deserializes an Object of class MyClass from its JSON representation */
   public static MyClass fromString(String jsonRepresentation) {
           ObjectMapper mapper = new ObjectMapper(); //Jackson's JSON marshaller
           MyClass o= null;
           try {
                   o = mapper.readValue(jsonRepresentation, MyClass.class );
           } catch (IOException e) {
                    throw new WebApplicationException()
           }
           return o;
   }
}

URI http://my-server.com/myService?a={"foo":1, "bar":2}&a={"foo":100, "bar":200} в этом случае будет десериализован в массив, состоящий из двух объектов MyClass.

2019 комментарий: Видя, что этот ответ все еще получает некоторые попадания в 2019 году,Я чувствую, что должен прокомментировать.Оглядываясь назад, я бы не рекомендовал вариант 2, поскольку выполнение этих шагов только для того, чтобы иметь возможность выполнять вызовы GET, добавляет сложности, которая, вероятно, того не стоит.Если ваш сервис принимает такие сложные входные данные, вы, вероятно, не сможете использовать кэширование на стороне клиента из-за количества перестановок вашего ввода.Я бы просто настроил правильные заголовки Cross-Origin-Sharing (CORS) на сервере и POST-ввод.Затем сконцентрируйтесь на кэшировании всего, что можете на сервере.

3 голосов
/ 21 июня 2018

Принятый ответ отсутствует @BeanParam.Подробнее см. https://docs.jboss.org/resteasy/docs/3.0-rc-1/javadocs/javax/ws/rs/BeanParam.html.Это позволяет вам определять параметры запроса внутри объекта-оболочки.Например,

public class TestPOJO {

    @QueryParam("someQueryParam")
    private boolean someQueryParam;

    public boolean isSomeQueryParam() {
        return someQueryParam;
    }

    public boolean setSomeQueryParam(boolean value) {
        this.someQueryParam = value;
    }
}

... // inside the Resource class
@GET
@Path("test")
public Response getTest(@BeanParam TestPOJO testPOJO) {
    ...
}
1 голос
/ 15 января 2016

лучшее и простое решение - отправить ваш объект в виде строки json, а на стороне сервера реализовать метод, который будет декодировать этот json и отображать его в указанном объекте в соответствии с вашими потребностями ... и да, лучше использовать POST.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...