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
REST service that accepts and returns object. How to write 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 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
API,以及从调用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()
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)
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 MessageBodyReader
that knows how to unmarshal JSON to your POJO type. Hymanson provides a MessageBodyReader
in it's Hymanson-jaxrs-json-provider
dependency
您所做的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-Hymanson
for Jersey or resteasy-Hymanson-provider
for Resteasy. But they are still using the underlying Hymanson-jaxrs-json-provider
.
大多数实现都会为此模块提供一个包装器,例如jersey-media-json-Hymanson
Jersey 或resteasy-Hymanson-provider
Resteasy。但他们仍在使用底层Hymanson-jaxrs-json-provider
.
That being said, once you have that module on the classpath, is shouldbe automatically registered, so the MessageBodyReader
will 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.Builder
methods. For instance
至于发布/发送数据,您可以再次查看不同的Invocation.Builder
方法。例如
Invocation.Builder builder = target.request();
If we want to post, look at the different post
methods available. We can use
如果我们要发布,请查看post
可用的不同方法。我们可以用
Response post(Entity<?> entity)
- Our request might look something likeResponse response = builder.post(Entity.json(myPojo));
You'll notice the
Entity
. All thepost
methods accept anEntity
, and this is how the request will know what type the entity body should be, and the client will invoke the approriateMessageBodyWriter
as 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 aResponse
. We could doMyPojo 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 Response
object 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 @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
顺便说一句,对于您显示的特定代码,您很可能希望将其设为@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);