REST, которая принимает и возвращает объект. Как написать клиент?

Я объявил две веб-службы REST. Тот, который просто возвращает объект. И другое, которое принимает объект и возвращает другой объект. Используется POJO Order.java.

@XmlRootElement
public class Order {

   private String id;

   private String description;

   public Order() {
   }

   @XmlElement
   public String getId() {
          return id;
   }
   @XmlElement
   public String getDescription() {
          return description;
   }

    // Other setters and methods
}

Webservice определяется как

@Path("/orders")
public class OrdersService {
// Return the list of orders for applications with json or xml formats
@Path("/oneOrder")
@GET
@Produces({MediaType.APPLICATION_JSON})
public  Order getOrder_json() {
  System.out.println("inside getOrder_json");
  Order o1 = OrderDao.instance.getOrderFromId("1");
  System.out.println("about to return one order");
  return o1;
}

@Path("/writeAndIncrementOrder")
@GET
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public  Order writeAndIncrementOrder(Order input) {
  System.out.println("inside writeAndIncrementOrder");
  Order o1 = new Order();
  o1.setId(input.getId()+1000);
  o1.setDescription(input.getDescription()+"10000");
  System.out.println("about to return one order");
  return o1;
 }

Я мог написать код клиента, чтобы вызвать веб-службу, которая ничего не принимает, но возвращает объект. Код клиента выглядит следующим образом

import java.net.URI;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.jersey.client.ClientConfig;

public class Test {

public static void main(String[] args) {

WebTarget target2 = client.target(getBaseURI()).path("rest").path("orders");
String o2 = target2.path("oneOrder").request().accept(MediaType.APPLICATION_JSON).get(String.class);
        System.out.println(o2);
}

private static URI getBaseURI() {
    return UriBuilder.fromUri("http://localhost:8090/FirstRESTProject").build();
  }

Но я не понимаю, как вызвать другую службу, которая принимает, а также возвращает объект. Я пробовал разные решения в Интернете. Но для меня ничего не работало. Некоторое решение работает только для отправки объекта, а некоторые работают только для принятия. Но ни один из них не работал для выполнения обоих вызовов.

ИЗМЕНИТЬ Как было предложено ниже, я зарегистрировал JacksonJaxbJsonProvider.class. Но автоматическое преобразование в объект Order не происходит.

        String o2 = target2.path("oneOrder").request().accept(MediaType.APPLICATION_JSON).get(String.class);

        client.register(JacksonJaxbJsonProvider.class);
        Order o4 = target2.path("oneOrder").request().accept(MediaType.APPLICATION_JSON).get(Order.class);

В вышеприведенной программе я успешно получаю строку как { "id": "1", "description": "Это 1-й порядок" } Но получение прямой ошибки объекта вызывает ошибку  MessageBodyReader не найден для типа media = application/json, type = class shopping.cart.om.Order, genericType = класс shopping.cart.om.Order.

Ответ 1

Если вам потребуется немного времени, чтобы понять WebTarget API, а также различные типы, возвращаемые из вызовов WebTarget, вы должны лучше понять, как совершать звонки. Это может быть немного запутанным, так как почти весь пример использует метод цепочки, поскольку это очень удобный способ, но при этом вы пропускаете все фактические классы, участвующие в создании и отправке запроса. Пусть немного сломается

WebTarget target = client.target(getBaseURI()).path("rest").path("orders");

WebTarget.path() просто возвращает WebTarget. Ничего интересного нет.

target.path("oneOrder").request().accept(MediaType.APPLICATION_JSON).get(String.class)

  • WebTarget.request() возвращает Invocation.Builder
  • Invocation.Builder.accept(..) возвращает Invocation.Builder
  • Invocation.Builder.get() вызывает свой суперкласс SyncInvoker.get(), который делает фактический запрос и возвращает тип, на основе аргумента мы предоставить get(Class returnType)

То, что вы делаете с get(String.class), означает, что поток ответа следует десериализовать в ответ типа Sting. Это не проблема, поскольку JSON по своей сути является просто строкой. Но если вы хотите развязать его с POJO, вам нужно иметь MessageBodyReader, который знает, как развязать JSON с вашим типом POJO. Джексон предоставляет MessageBodyReader в нем jackson-jaxrs-json-provider зависимость

<dependency>
  <groupId>com.fasterxml.jackson.jaxrs</groupId>
  <artifactId>jackson-jaxrs-json-provider</artifactId>
  <version>2.4.0</version>
</dependency>

В большинстве реализаций будет использоваться оболочка для этого модуля, например jersey-media-json-jackson для Джерси или resteasy-jackson-provider для Resteasy. Но они все еще используют базовый jackson-jaxrs-json-provider.

При этом, если у вас есть этот модуль в пути к классам, он должен быть автоматически зарегистрирован, поэтому будет доступен MessageBodyReader. Если нет, вы можете зарегистрировать его явно с клиентом, например client.register(JacksonJaxbJsonProvider.class). После того, как вы настроили поддержку Jackson, вы можете просто сделать что-то вроде

MyPojo myPojo = client.target(..).path(...).request().accept(..).get(MyPojo.class);

Что касается отправки/отправки данных, вы можете снова просмотреть различные методы Invocation.Builder. Например,

Invocation.Builder builder = target.request();

Если мы хотим опубликовать, просмотрите различные методы post. Мы можем использовать

  • Response post(Entity<?> entity) - Наш запрос может выглядеть примерно как

    Response response = builder.post(Entity.json(myPojo));
    

    Вы заметите Entity. Все методы post принимают Entity, и именно так запрос будет знать, каким типом должен быть объект сущности, и клиент будет вызывать approriate MessageBodyWriter, а также установить соответствующий заголовок

  • <T> T post(Entity<?> entity, Class<T> responseType) - Там есть другая перегрузка, где мы можем указать тип, который нужно развязать, вместо возврата Response. Мы могли бы сделать

    MyPojo myPojo = builder.post(Entity.json(myPojo), MyPojo.class)
    

Обратите внимание, что с помощью Response мы вызываем его метод readEntity(Class pojoType) для чтения из тела Response, тела объекта. Преимущество этого заключается в том, что объект Response поставляется с большим количеством полезной информации, которую мы можем использовать, например, заголовками и т.д. Лично я всегда получаю Response

    Response response = builder.get();
    MyPojo pojo = response.readEntity(MyPojo.class);

Как в стороне, для вашего конкретного кода, который вы показываете, вы, скорее всего, хотите сделать его методом @POST. Помните @GET в основном для получения данных, PUT для обновления и post для создания. Это хорошее эмпирическое правило, которое следует придерживаться при первом запуске. Таким образом, вы можете изменить метод на

@Path("orders")
public class OrdersResource  {

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes({MediaType.APPLICATION_JSON})
    public Response createOrder(@Context UriInfo uriInfo, Order input) {
       Order order = orderService.createOrder(input);
       URI uri = uriInfo.getAbsolutePathBuilder().path(order.getId()).build();
       return Response.create(uri).entity(order).build();
    }
}

Затем вы можете сделать

WebTarget target = client.target(BASE).path("orders");
Response response = target.request().accept(...).post(Entity.json(order));
Order order = response.readEntity(Order.class);

Ответ 2

Вместо этого вы должны использовать POST или PUT GET

попробуйте этот код

final Client client = new Client();
    final Order input = new Order();
    input.setId("1");
    input.setDescription("description");
    final Order output = client.resource(
"http://localhost:8080/orders/writeAndIncrementOrder").
            header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).
            entity(input).post(Order.class);