web-dev-qa-db-fra.com

REST service qui accepte et retourne un objet. Comment écrire un client?

J'ai déclaré deux REST services Web. L'un qui renvoie simplement un objet. Et l'autre qui accepte un objet et renvoie un autre objet. POJO Order.Java est utilisé.

@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 est défini comme

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

Je pourrais écrire du code client pour appeler le service Web qui n'accepte rien mais renvoie un objet. Le code client est le suivant

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

Mais je ne comprends pas comment appeler un autre service qui accepte ainsi que renvoie un objet. J'ai essayé différentes solutions proposées sur internet. Mais rien n'a fonctionné pour moi. Certaines solutions ne fonctionnent que pour l'envoi d'objet et certaines ne fonctionnent que pour l'acceptation. Mais aucun n'a travaillé pour faire les deux en un seul appel.

EDIT Comme suggéré dans la réponse ci-dessous, j'ai enregistré JacksonJaxbJsonProvider.class Mais la conversion automatique en objet Order ne se produit pas.

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

Dans le programme ci-dessus, j'ai réussi à obtenir la chaîne sous la forme {"id": "1", "description": "Il s'agit du premier ordre"} .cart.om.Order, genericType = classe shopping.cart.om.Order.

13
Kaushik Lele

Si vous prenez un peu de temps pour comprendre l'API WebTarget , ainsi que les différents types renvoyés par les appels à la méthode de WebTarget, vous devriez mieux comprendre comment passer des appels. Cela peut être un peu déroutant, car presque tous les exemples utilisent le chaînage de méthode, car c'est un moyen très pratique, mais ce faisant, vous manquez toutes les classes réelles impliquées dans la création et l'envoi de la demande. Laisse le décomposer un peu

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

WebTarget.path() renvoie simplement le WebTarget. Rien d'intéressant là-bas.

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

  • WebTarget.request() renvoie Invocation.Builder
  • Invocation.Builder.accept(..) renvoie Invocation.Builder
  • Invocation.Builder.get() appelle sa super classe SyncInvoker.get() , qui fait la requête réelle et retourne un type, basé sur l'argument que nous fournissons à get(Class returnType)

Ce que vous faites avec get(String.class), c'est que le flux de réponse doit être désérialisé en une réponse de type Sting. Ce n'est pas un problème, car JSON est intrinsèquement juste une chaîne. Mais si vous voulez le démarsaler en POJO, vous devez avoir un MessageBodyReader qui sait comment démarshal JSON à votre type POJO. Jackson fournit un MessageBodyReader dans sa dépendance jackson-jaxrs-json-provider

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

La plupart des implémentations fourniront un wrapper pour ce module, comme jersey-media-json-jackson Pour Jersey ou resteasy-jackson-provider Pour Resteasy. Mais ils utilisent toujours le sous-jacent jackson-jaxrs-json-provider.

Cela étant dit, une fois que vous avez ce module sur le chemin de classe, est devrait être automatiquement enregistré, donc le MessageBodyReader sera disponible. Sinon, vous pouvez l'enregistrer explicitement auprès du client, comme client.register(JacksonJaxbJsonProvider.class). Une fois que le support Jackson est configuré, vous pouvez simplement faire quelque chose comme

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

En ce qui concerne la publication/l'envoi de données, vous pouvez à nouveau regarder les différentes méthodes Invocation.Builder. Par exemple

Invocation.Builder builder = target.request();

Si nous voulons publier, regardez les différentes méthodes post disponibles. On peut utiliser

  • Response post(Entity<?> entity) - Notre requête pourrait ressembler à quelque chose

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

    Vous remarquerez Entity . Toutes les méthodes post acceptent un Entity, et c'est ainsi que la requête saura de quel type doit être le corps d'entité, et le client invoquera le MessageBodyWriter approprié ainsi que définir l'en-tête approprié

  • <T> T post(Entity<?> entity, Class<T> responseType) - Il y a une autre surcharge, où nous pouvons spécifier le type à démarsaler dans, au lieu de récupérer un Response. Nous pourrions faire

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

Notez qu'avec Response, nous appelons sa méthode readEntity(Class pojoType) pour lire le Response, le corps de l'entité. L'avantage de ceci est que l'objet Response est livré avec beaucoup d'informations utiles que nous pouvons utiliser, comme des en-têtes et autres. Personnellement, je reçois toujours le Response

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

En passant, pour votre code particulier que vous montrez, vous voudrez probablement en faire une méthode @POST. N'oubliez pas que @GET Sert principalement à récupérer les données, PUT à mettre à jour et POST à créer. C'est une bonne règle de base à respecter lors du premier démarrage. Vous pouvez donc changer la méthode en

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

Ensuite, vous pouvez faire

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

Vous devez utiliser POST ou PUT à la place GET

essayez ce 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);
4
Dmitry Zaytsev