接受并返回对象的REST服务.怎么写客户端?

Kau*_*ele 13 java rest jersey jersey-client

我已经声明了两个REST Web服务.一个简单地返回一个对象.和其他接受一个对象并返回另一个对象.使用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
}
Run Code Online (Sandbox Code Playgroud)

Web服务定义为

@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;
 }
Run Code Online (Sandbox Code Playgroud)

我可以编写客户端代码来调用除了返回对象之外不接受任何操作的Web服务.客户端代码如下

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();
  }
Run Code Online (Sandbox Code Playgroud)

但我不明白如何调用接受以及返回对象的其他服务.我在互联网上尝试了不同的解决方案 但没有什么对我有用.某些解决方案仅适用于发送对象,而某些解决方案仅适用于接受.但是没有人能在一次通话中同时做到这两点.

编辑 如下面的答案所示,我注册了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);
Run Code Online (Sandbox Code Playgroud)

在上面的程序中我成功获取字符串为{"id":"1","description":"这是第一顺序"}但是获取直接对象抛出错误MessageBodyReader未找到媒体类型= application/json,type = class shopping .cart.om.Order,genericType = class shopping.cart.om.Order.

Pau*_*tha 15

如果您花一点时间来理解WebTargetAPI,以及从调用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本质上只是一个String.但是如果你想将它解组为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>
Run Code Online (Sandbox Code Playgroud)

大多数实现都会为这个模块提供一个包装器,比如jersey-media-json-jacksonJersey或resteasy-jackson-providerResteasy.但他们仍在使用底层证券jackson-jaxrs-json-provider.

话虽这么说,一旦你在类路径上有这个模块,就应该自动注册,这样MessageBodyReader就可以了.如果没有,您可以明确地向客户注册,例如client.register(JacksonJaxbJsonProvider.class).一旦你配置了Jackson支持,那么你可以简单地做一些事情

MyPojo myPojo = client.target(..).path(...).request().accept(..).get(MyPojo.class);
Run Code Online (Sandbox Code Playgroud)

至于发布/发送数据,您可以再次查看不同的Invocation.Builder方法.例如

Invocation.Builder builder = target.request();
Run Code Online (Sandbox Code Playgroud)

如果我们想发布,请查看post可用的不同方法.我们可以用

  • Response post(Entity<?> entity) - 我们的要求可能看起来像

    Response response = builder.post(Entity.json(myPojo));
    
    Run Code Online (Sandbox Code Playgroud)

    你会注意到的Entity.所有post方法都接受一个Entity,这就是请求将如何知道实体主体应该是什么类型,客户端将调用approriate MessageBodyWriter以及设置适当的头

  • <T> T post(Entity<?> entity, Class<T> responseType)- 还有另一个重载,我们可以指定要解组的类型,而不是返回a Response.我们能做到

    MyPojo myPojo = builder.post(Entity.json(myPojo), MyPojo.class)
    
    Run Code Online (Sandbox Code Playgroud)

请注意Response,我们将其readEntity(Class pojoType)方法Response称为实体主体.这样做的好处是,Response对象带有许多我们可以使用的有用信息,比如标题等.就个人而言,我总是得到Response

    Response response = builder.get();
    MyPojo pojo = response.readEntity(MyPojo.class);
Run Code Online (Sandbox Code Playgroud)

顺便说一句,对于您正在展示的特定代码,您很可能希望将其作为一种@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

那你可以做

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


Dmi*_*sev 4

您应该使用 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);
Run Code Online (Sandbox Code Playgroud)