Une introduction à la haine printanière

Haut REST

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

1. Vue d'ensemble

Cet article explique le processus de création d'un service Web REST basé sur l'hypermédia à l'aide du projet Spring HATEOAS.

2. Printemps-HATEOAS

Le projet Spring HATEOAS est une bibliothèque d'API que nous pouvons utiliser pour créer facilement des représentations REST qui suivent le principe de HATEOAS (Hypertext as the Engine of Application State).

De manière générale, le principe implique que l'API doit guider le client à travers l'application en renvoyant des informations pertinentes sur les prochaines étapes potentielles, avec chaque réponse.

Dans cet article, nous allons créer un exemple en utilisant Spring HATEOAS dans le but de découpler le client et le serveur, et en théorie permettant à l'API de modifier son schéma d'URI sans casser les clients.

3. Préparation

Tout d'abord, ajoutons la dépendance Spring HATEOAS:

 org.springframework.boot spring-boot-starter-hateoas 2.1.4.RELEASE 

Si nous n'utilisons pas Spring Boot, nous pouvons ajouter les bibliothèques suivantes à notre projet:

 org.springframework.hateoas spring-hateoas 0.25.1.RELEASE   org.springframework.plugin spring-plugin-core 1.2.0.RELEASE 

Comme toujours, nous pouvons rechercher les dernières versions des dépendances de démarrage HATEOAS, spring-hateoas et spring-plugin-core dans Maven Central.

Ensuite, nous avons la ressource Client sans le support Spring HATEOAS:

public class Customer { private String customerId; private String customerName; private String companyName; // standard getters and setters } 

Et nous avons une classe de contrôleur sans support Spring HATEOAS:

@RestController @RequestMapping(value = "/customers") public class CustomerController { @Autowired private CustomerService customerService; @GetMapping("/{customerId}") public Customer getCustomerById(@PathVariable String customerId) { return customerService.getCustomerDetail(customerId); } } 

Enfin, la représentation des ressources client :

{ "customerId": "10A", "customerName": "Jane", "customerCompany": "ABC Company" } 

4. Ajout de la prise en charge de HATEOAS

Dans un projet Spring HATEOAS, nous n'avons pas besoin de rechercher le contexte Servlet ni de concaténer la variable de chemin à l'URI de base.

Au lieu de cela, Spring HATEOAS propose trois abstractions pour créer l'URI: RepresentationModel, Link et WebMvcLinkBuilder . Nous pouvons les utiliser pour créer les métadonnées et les associer à la représentation des ressources.

4.1. Ajout de la prise en charge hypermédia à une ressource

Le projet fournit une classe de base appelée RepresentationModel dont hériter lors de la création d'une représentation de ressource:

public class Customer extends RepresentationModel { private String customerId; private String customerName; private String companyName; // standard getters and setters } 

La ressource Customer s'étend de la classe RepresentationModel pour hériter de la méthode add () . Ainsi, une fois que nous avons créé un lien, nous pouvons facilement définir cette valeur sur la représentation de la ressource sans ajouter de nouveaux champs.

4.2. Créer des liens

Spring HATEOAS fournit un objet Link pour stocker les métadonnées (emplacement ou URI de la ressource).

Tout d'abord, nous allons créer un lien simple manuellement:

Link link = new Link("//localhost:8080/spring-security-rest/api/customers/10A"); 

L' objet Link suit la syntaxe du lien Atom et se compose d'un rel qui identifie la relation avec la ressource et de l' attribut href qui est le lien lui-même.

Voici à quoi ressemble la ressource Client maintenant qu'elle contient le nouveau lien:

{ "customerId": "10A", "customerName": "Jane", "customerCompany": "ABC Company", "_links":{ "self":{ "href":"//localhost:8080/spring-security-rest/api/customers/10A" } } } 

L'URI associé à la réponse est qualifié de lien automatique . La sémantique de la relation de soi est claire - c'est simplement l'emplacement canonique auquel la ressource peut être accédée.

4.3. Créer de meilleurs liens

Une autre abstraction très importante offerte par la bibliothèque est le WebMvcLinkBuilder - qui simplifie la construction d'URI en évitant les liens codés en dur.

L'extrait de code suivant montre la création du lien automatique client à l'aide de la classe WebMvcLinkBuilder :

linkTo(CustomerController.class).slash(customer.getCustomerId()).withSelfRel(); 

Regardons:

  • la méthode linkTo () inspecte la classe du contrôleur et obtient son mappage racine
  • la méthode slash () ajoute la valeur customerId comme variable de chemin du lien
  • enfin, le withSelfMethod () qualifie la relation comme un auto-lien

5. Relations

Dans la section précédente, nous avons montré une relation d'auto-référencement. Cependant, des systèmes plus complexes peuvent également impliquer d'autres relations.

Par exemple, un client peut avoir une relation avec les commandes. Modélisons également la classe Order en tant que ressource:

public class Order extends RepresentationModel { private String orderId; private double price; private int quantity; // standard getters and setters } 

À ce stade, nous pouvons étendre le CustomerController avec une méthode qui renvoie toutes les commandes d'un client particulier:

@GetMapping(value = "/{customerId}/orders", produces = { "application/hal+json" }) public CollectionModel getOrdersForCustomer(@PathVariable final String customerId) { List orders = orderService.getAllOrdersForCustomer(customerId); for (final Order order : orders) { Link selfLink = linkTo(methodOn(CustomerController.class) .getOrderById(customerId, order.getOrderId())).withSelfRel(); order.add(selfLink); } Link link = linkTo(methodOn(CustomerController.class) .getOrdersForCustomer(customerId)).withSelfRel(); CollectionModel result = new CollectionModel(orders, link); return result; } 

Our method returns a CollectionModel object to comply with the HAL return type, as well as a “_self” link for each of the orders and the full list.

An important thing to notice here is that the hyperlink for the customer orders depends on the mapping of getOrdersForCustomer() method. We'll refer to these types of links as method links and show how the WebMvcLinkBuilder can assist in their creation.

6. Links to Controller Methods

The WebMvcLinkBuilder offers rich support for Spring MVC Controllers. The following example shows how to build HATEOAS hyperlinks based on the getOrdersForCustomer() method of the CustomerController class:

Link ordersLink = linkTo(methodOn(CustomerController.class) .getOrdersForCustomer(customerId)).withRel("allOrders"); 

The methodOn() obtains the method mapping by making dummy invocation of the target method on the proxy controller and sets the customerId as the path variable of the URI.

7. Spring HATEOAS in Action

Let's put the self-link and method link creation all together in a getAllCustomers() method:

@GetMapping(produces = { "application/hal+json" }) public CollectionModel getAllCustomers() { List allCustomers = customerService.allCustomers(); for (Customer customer : allCustomers) { String customerId = customer.getCustomerId(); Link selfLink = linkTo(CustomerController.class).slash(customerId).withSelfRel(); customer.add(selfLink); if (orderService.getAllOrdersForCustomer(customerId).size() > 0) { Link ordersLink = linkTo(methodOn(CustomerController.class) .getOrdersForCustomer(customerId)).withRel("allOrders"); customer.add(ordersLink); } } Link link = linkTo(CustomerController.class).withSelfRel(); CollectionModel result = new CollectionModel(allCustomers, link); return result; }

Next, let's invoke the getAllCustomers() method:

curl //localhost:8080/spring-security-rest/api/customers 

And examine the result:

{ "_embedded": { "customerList": [{ "customerId": "10A", "customerName": "Jane", "companyName": "ABC Company", "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A" }, "allOrders": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/orders" } } },{ "customerId": "20B", "customerName": "Bob", "companyName": "XYZ Company", "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/20B" }, "allOrders": { "href": "//localhost:8080/spring-security-rest/api/customers/20B/orders" } } },{ "customerId": "30C", "customerName": "Tim", "companyName": "CKV Company", "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/30C" } } }] }, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers" } } }

Within each resource representation, there is a self link and the allOrders link to extract all orders of a customer. If a customer doesn't have orders, then the link for orders won't appear.

Cet exemple montre comment Spring HATEOAS favorise la découverte des API dans un service Web de repos. Si le lien existe, le client peut le suivre et récupérer toutes les commandes d'un client:

curl //localhost:8080/spring-security-rest/api/customers/10A/orders 
{ "_embedded": { "orderList": [{ "orderId": "001A", "price": 150, "quantity": 25, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/001A" } } },{ "orderId": "002A", "price": 250, "quantity": 15, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/002A" } } }] }, "_links": { "self": { "href": "//localhost:8080/spring-security-rest/api/customers/10A/orders" } } }

8. Conclusion

Dans ce didacticiel, nous avons expliqué comment créer un service Web Spring REST basé sur l'hypermédia à l'aide du projet Spring HATEOAS .

Dans l'exemple, nous voyons que le client peut avoir un point d'entrée unique dans l'application et que d'autres actions peuvent être entreprises en fonction des métadonnées dans la représentation de la réponse.

Cela permet au serveur de modifier son schéma URI sans interrompre le client. En outre, l'application peut publier de nouvelles fonctionnalités en mettant de nouveaux liens ou URI dans la représentation.

Enfin, l'implémentation complète de cet article se trouve dans le projet GitHub.

REST bas

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS