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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29046784/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:14:54  来源:igfitidea点击:

REST service that accepts and returns object. How to write client?

javarestjerseyjersey-client

提问by Kaushik Lele

I have declared two REST web services. One which simply returns a object. And other which accepts an object and returns another object. POJO Order.java is used.

我已经声明了两个 REST Web 服务。一个简单地返回一个对象。而 other 接受一个对象并返回另一个对象。使用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 is defined as

网络服务定义为

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

I could write client code to call the web service that does not accept anything but returns object. Client code is as follows

我可以编写客户端代码来调用不接受任何内容但返回对象的 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();
  }

But I do not understand how to call other service which accepts as well as returns object. I tried different solutions given on internet. But nothing worked for me. Some solution works only for sending object and some works only for accepting. But none worked for doing both in one call.

但我不明白如何调用其他接受和返回对象的服务。我尝试了互联网上给出的不同解决方案。但没有什么对我有用。有些解决方案仅适用于发送对象,有些仅适用于接受。但是没有一个可以同时完成这两个任务。

EDITAs suggested in below answer I registered HymansonJaxbJsonProvider.class But auto-conversion into Order object is not happening.

编辑正如下面的答案中所建议的,我注册了 HymansonJaxbJsonProvider.class 但是没有自动转换为 Order 对象。

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

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

In above program I successfully get string as {"id":"1","description":"This is the 1st order"} But getting direct object throws error MessageBodyReader not found for media type=application/json, type=class shopping.cart.om.Order, genericType=class shopping.cart.om.Order.

在上面的程序中,我成功地将字符串作为 {"id":"1","description":"This is the 1st order"} 但是获取直接对象抛出错误 MessageBodyReader not found for media type=application/json, type=class shopping .cart.om.Order,genericType=class shopping.cart.om.Order。

回答by Dmitry Zaytsev

You should use POST or PUT instead GET

您应该使用 POST 或 PUT 代替 GET

try this code

试试这个代码

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

回答by Paul Samsotha

If you take a little bit of time to understand the WebTargetAPI, as well as the different types returned from calls to WebTarget's method, you should get a better understanding of how to make calls. It may be a little confusing, as almost all the example use method chaining, as it's a very convenient way, but doing this, you miss all the actual classes involved in create and sending the request. Let break it down a bit

如果您花一点时间了解WebTargetAPI,以及从调用WebTarget的方法返回的不同类型,您应该对如何进行调用有更好的了解。这可能有点令人困惑,因为几乎所有示例都使用方法链,因为这是一种非常方便的方式,但是这样做,您会错过创建和发送请求所涉及的所有实际类。让我们分解一下

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

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

WebTarget.path()simply returns the WebTarget. Nothing interesting there.

WebTarget.path()简单地返回WebTarget. 没有什么有趣的。

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

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

  • WebTarget.request()returns Invocation.Builder
  • Invocation.Builder.accept(..)returns Invocation.Builder
  • Invocation.Builder.get()calls its super class's SyncInvoker.get(), which makes the actual request, and returns a type, based on the argument we provide to get(Class returnType)
  • WebTarget.request()返回 Invocation.Builder
  • Invocation.Builder.accept(..)返回 Invocation.Builder
  • Invocation.Builder.get()调用其超类的SyncInvoker.get(),它发出实际请求,并根据我们提供给的参数返回一个类型get(Class returnType)

What you're doing with get(String.class)is saying that the response stream should be deserialized into a Sting type response. This is not a problem, as JSON is inherently just a String. But if you want to unmarshal it to a POJO, then you need to have a MessageBodyReaderthat knows how to unmarshal JSON to your POJO type. Hymanson provides a MessageBodyReaderin it's Hymanson-jaxrs-json-providerdependency

您所做的get(String.class)是说响应流应该反序列化为 Sting 类型的响应。这不是问题,因为 JSON 本质上只是一个字符串。但是,如果您想将其解组为 POJO,那么您需要MessageBodyReader知道如何将 JSON 解组为您的 POJO 类型。Hyman逊提供了一个MessageBodyReader在它的Hymanson-jaxrs-json-provider依赖

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

Most implementations will provider a wrapper for this module, like jersey-media-json-Hymansonfor Jersey or resteasy-Hymanson-providerfor Resteasy. But they are still using the underlying Hymanson-jaxrs-json-provider.

大多数实现都会为此模块提供一个包装器,例如jersey-media-json-HymansonJersey 或resteasy-Hymanson-providerResteasy。但他们仍在使用底层Hymanson-jaxrs-json-provider.

That being said, once you have that module on the classpath, is shouldbe automatically registered, so the MessageBodyReaderwill be available. If not you can register it explicitly with the client, like client.register(HymansonJaxbJsonProvider.class). Once you have the Hymanson support configured, then you can simply do something like

话虽如此,一旦您在类路径上拥有该模块,就应该自动注册,因此MessageBodyReader将可用。如果没有,您可以向客户端显式注册它,例如client.register(HymansonJaxbJsonProvider.class). 一旦您配置了 Hymanson 支持,您就可以简单地执行以下操作

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

As for posting/sending data, you can again look at the different Invocation.Buildermethods. For instance

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

Invocation.Builder builder = target.request();

If we want to post, look at the different postmethods available. We can use

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

  • Response post(Entity<?> entity)- Our request might look something like

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

    You'll notice the Entity. All the postmethods accept an Entity, and this is how the request will know what type the entity body should be, and the client will invoke the approriate MessageBodyWriteras well as set the appropriate header

  • <T> T post(Entity<?> entity, Class<T> responseType)- There's another overload, where we can specify the type to unmarshal into, instead of getting back a Response. We could do

    MyPojo myPojo = builder.post(Entity.json(myPojo), MyPojo.class)
    
  • Response post(Entity<?> entity)- 我们的请求可能看起来像

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

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

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

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

Note that with Response, we call its readEntity(Class pojoType)method to read from the Response, the entity body. The advantage of this, is that the Responseobject comes with a lot of useful information we can use, like headers and such. Personally, I always get the Response

请注意,使用Response,我们调用其readEntity(Class pojoType)方法从Response,实体主体中读取。这样做的好处是,该Response对象带有许多我们可以使用的有用信息,例如标题等。就个人而言,我总是得到Response

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

As an aside, for your particular code you are showing, you most likely want to make it a @POSTmethod. Remember @GETis mainly for retrieving data, PUTfor updating, and POSTfor creating. That is a good rule of thumb to stick to, when first starting out. So you might change the method to

顺便说一句,对于您显示的特定代码,您很可能希望将其设为@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();
    }
}

Then you can do

然后你可以做

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