接受并返回对象的REST服务。怎么写客户端? [英] REST service that accepts and returns object. How to write client?
问题描述
我已经宣布了两个REST Web服务。一个只返回一个对象的人。和其他接受对象并返回另一个对象。
使用POJO Order.java。
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.
@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定义为
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;
}
我可以编写客户端代码来调用不接受任何内容的Web服务返回对象。客户端代码如下
I could write client code to call the web service that does not accept anything but returns object. Client code is as follows
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.
编辑
如下面的建议,我注册了JacksonJaxbJsonProvider.class但是转换为订单对象没有发生。
EDIT As suggested in below answer I registered JacksonJaxbJsonProvider.class But auto-conversion into Order object is not happening.
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: 这是第一个订单}但是获取直接对象会抛出错误
找不到媒体类型= application / json的MessageBodyReader,类型= class shopping.cart.om.Order,genericType = class shopping.cart.om.Order 。
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.
推荐答案
如果你花一点时间来理解 WebTarget
API,以及通过调用 WebTarget
的方法返回的不同类型,你应该更好地理解如何进行调用。这可能有点令人困惑,因为几乎所有示例都使用方法链接,因为这是一种非常方便的方式,但是这样做,您会错过创建和发送请求所涉及的所有实际类。让我们分解一下
If you take a little bit of time to understand the WebTarget
API, 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
WebTarget target = client.target(getBaseURI())。path(rest )。path(orders);
WebTarget.path( )
只返回 WebTarget
。没有什么有趣的。
WebTarget.path()
simply returns the WebTarget
. Nothing interesting there.
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)
WebTarget.request()
returnsInvocation.Builder
Invocation.Builder.accept(..)
returnsInvocation.Builder
Invocation.Builder.get()
calls its super class'sSyncInvoker.get()
, which makes the actual request, and returns a type, based on the argument we provide toget(Class returnType)
你正在做什么 get(String.class)
表示响应流应该被反序列化为Sting类型的响应。这不是问题,因为JSON本质上只是一个String。但是如果你想将它解组为POJO,那么你需要知道如何将JSON解组为POJO类型的 MessageBodyReader
。杰克逊在 <$ c}中提供 MessageBodyReader
$ c> jackson-jaxrs-json-provider 依赖
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 MessageBodyReader
that knows how to unmarshal JSON to your POJO type. Jackson provides a MessageBodyReader
in it's jackson-jaxrs-json-provider
dependency
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.0</version>
</dependency>
大多数实现都会为这个模块提供一个包装器,比如 jersey-media-泽西的json-jackson
或Resteasy的 resteasy-jackson-provider
。但他们仍在使用基础 jackson-jaxrs-json-provider
。
Most implementations will provider a wrapper for this module, like jersey-media-json-jackson
for Jersey or resteasy-jackson-provider
for Resteasy. But they are still using the underlying jackson-jaxrs-json-provider
.
这就是说,一旦你有了类路径上的那个模块应自动注册,因此 MessageBodyReader
将可用。如果没有,您可以使用客户端明确注册它,例如 client.register(JacksonJaxbJsonProvider.class)
。一旦你配置了Jackson支持,那么你可以简单地做一些事情,比如
That being said, once you have that module on the classpath, is should be automatically registered, so the MessageBodyReader
will be available. If not you can register it explicitly with the client, like client.register(JacksonJaxbJsonProvider.class)
. Once you have the Jackson support configured, then you can simply do something like
MyPojo myPojo = client.target(..).path(...).request().accept(..).get(MyPojo.class);
至于发布/发送数据,您可以再次查看不同的调用.Builder
方法。例如
As for posting/sending data, you can again look at the different Invocation.Builder
methods. For instance
Invocation.Builder builder = target.request();
如果我们想发布,请查看不同的 发布
方法可用。我们可以使用
If we want to post, look at the different post
methods available. We can use
-
回复帖子(实体<?>实体)
- 我们的请求可能类似于
Response post(Entity<?> entity)
- Our request might look something like
Response response = builder.post(Entity.json(myPojo));
你会注意到 实体
。所有 post
方法都接受实体
,这就是请求将知道实体主体应该是什么类型的方式,并且客户端将调用approriate MessageBodyWriter
并设置相应的标题
You'll notice the Entity
. All the post
methods 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 MessageBodyWriter
as well as set the appropriate header
< T> T post(实体<?>实体,Class< T> responseType)
- 还有另一个重载,我们可以在其中指定要解组的类型,而不是返回响应
。我们可以做到
<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)
请注意,响应
,我们称其 readEntity(类pojoType)
方法从实体主体 Response
中读取。这样做的好处是 Response
对象附带了许多我们可以使用的有用信息,比如标题等。就个人而言,我总是得到回复
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 Response
object comes with a lot of useful information we can use, like headers and such. Personally, I always get the Response
Response response = builder.get();
MyPojo pojo = response.readEntity(MyPojo.class);
顺便说一句,对于您展示的特定代码,您很可能希望将其设为 @POST
方法。记住 @GET
主要用于检索数据, PUT
用于更新, POST
用于创建。首次出发时,坚持这是一个很好的经验法则。因此,您可以将方法更改为
As an aside, for your particular code you are showing, you most likely want to make it a @POST
method. Remember @GET
is mainly for retrieving data, PUT
for updating, and POST
for creating. That is a good rule of thumb to stick to, when first starting out. So you might change the method to
@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);
这篇关于接受并返回对象的REST服务。怎么写客户端?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!