Découvrabilité de l'API REST et HATEOAS

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 se concentrera sur la découvrabilité de l'API REST, HATEOAS et des scénarios pratiques pilotés par des tests.

2. Pourquoi rendre l'API détectable

La découvrabilité d'une API est un sujet qui ne retient pas suffisamment l'attention bien méritée. En conséquence, très peu d'API fonctionnent correctement. C'est aussi quelque chose qui, s'il est fait correctement, peut rendre l'API non seulement RESTful et utilisable mais aussi élégante.

Pour comprendre la découvrabilité, nous devons comprendre la contrainte Hypermedia As The Engine Of Application State (HATEOAS). Cette contrainte d'une API REST concerne la découvrabilité totale des actions / transitions sur une Ressource d'Hypermédia (vraiment hypertexte), comme seul pilote de l'état de l'application.

Si l'interaction doit être conduite par l'API à travers la conversation elle-même, concrètement via l'hypertexte, alors il ne peut y avoir de documentation. Cela obligerait le client à faire des hypothèses qui sont en fait en dehors du contexte de l'API.

En conclusion, le serveur doit être suffisamment descriptif pour indiquer au client comment utiliser l'API via Hypertext uniquement. Dans le cas d'une conversation HTTP, nous pourrions y parvenir via l'en- tête Link .

3. Scénarios de découvrabilité (basés sur des tests)

Alors, qu'est-ce que cela signifie pour un service REST d'être détectable?

Tout au long de cette section, nous testerons des caractéristiques individuelles de découvrabilité à l'aide de Junit, soyez assuré et Hamcrest. Étant donné que le service REST a été préalablement sécurisé, chaque test doit d'abord s'authentifier avant de consommer l'API.

3.1. Découvrez les méthodes HTTP valides

Lorsqu'un service REST est utilisé avec une méthode HTTP non valide, la réponse doit être une méthode 405 NON AUTORISÉE.

L'API doit également aider le client à découvrir les méthodes HTTP valides qui sont autorisées pour cette ressource particulière. Pour cela, nous pouvons utiliser l' en-tête HTTP Autoriser dans la réponse:

@Test public void whenInvalidPOSTIsSentToValidURIOfResource_thenAllowHeaderListsTheAllowedActions(){ // Given String uriOfExistingResource = restTemplate.createResource(); // When Response res = givenAuth().post(uriOfExistingResource); // Then String allowHeader = res.getHeader(HttpHeaders.ALLOW); assertThat( allowHeader, AnyOf.anyOf( containsString("GET"), containsString("PUT"), containsString("DELETE") ) ); }

3.2. Découvrez l'URI de la ressource nouvellement créée

L'opération de création d'une nouvelle ressource doit toujours inclure l'URI de la ressource nouvellement créée dans la réponse. Pour cela, nous pouvons utiliser l' en-tête HTTP Location .

Maintenant, si le client effectue un GET sur cet URI, la ressource doit être disponible:

@Test public void whenResourceIsCreated_thenUriOfTheNewlyCreatedResourceIsDiscoverable() { // When Foo newResource = new Foo(randomAlphabetic(6)); Response createResp = givenAuth().contentType("application/json") .body(unpersistedResource).post(getFooURL()); String uriOfNewResource= createResp.getHeader(HttpHeaders.LOCATION); // Then Response response = givenAuth().header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .get(uriOfNewResource); Foo resourceFromServer = response.body().as(Foo.class); assertThat(newResource, equalTo(resourceFromServer)); }

Le test suit un scénario simple: créer une nouvelle ressource Foo , puis utiliser la réponse HTTP pour découvrir l'URI où la ressource est désormais disponible . Il effectue également un GET sur cet URI pour récupérer la ressource et la compare à l'original. Ceci pour vous assurer qu'il a été correctement enregistré.

3.3. Découvrez l'URI pour OBTENIR toutes les ressources de ce type

Lorsque nous OBTENONS une ressource Foo particulière , nous devrions être en mesure de découvrir ce que nous pouvons faire ensuite: nous pouvons lister toutes les ressources Foo disponibles. Ainsi, l'opération de récupération d'une ressource doit toujours inclure dans sa réponse l'URI où obtenir toutes les ressources de ce type.

Pour cela, nous pouvons à nouveau utiliser l'en- tête Link :

@Test public void whenResourceIsRetrieved_thenUriToGetAllResourcesIsDiscoverable() { // Given String uriOfExistingResource = createAsUri(); // When Response getResponse = givenAuth().get(uriOfExistingResource); // Then String uriToAllResources = HTTPLinkHeaderUtil .extractURIByRel(getResponse.getHeader("Link"), "collection"); Response getAllResponse = givenAuth().get(uriToAllResources); assertThat(getAllResponse.getStatusCode(), is(200)); }

Notez que le code de bas niveau complet pour extractURIByRel - responsable de l'extraction des URI par relation rel est affiché ici.

Ce test couvre le sujet épineux des relations de lien dans REST: l'URI pour récupérer toutes les ressources utilise la sémantique rel = ”collection” .

Ce type de relation de lien n'a pas encore été standardisé, mais est déjà utilisé par plusieurs microformats et proposé pour la standardisation. L'utilisation de relations de lien non standard ouvre la discussion sur les microformats et une sémantique plus riche dans les services Web RESTful.

4. Autres URI et microformats potentiellement découvrables

D'autres URI pourraient potentiellement être découverts via l'en- tête Link , mais les types de relations de lien existants ne le permettent pas sans passer à un balisage sémantique plus riche tel que la définition de relations de lien personnalisées, le protocole de publication Atom ou les microformats, qui seront le sujet d'un autre article.

Par exemple, le client doit être en mesure de découvrir l'URI pour créer de nouvelles ressources lors d'un GET sur une ressource spécifique. Malheureusement, il n'y a pas de relation de lien avec la sémantique de création de modèle .

Heureusement, c'est une pratique standard que l'URI de création est le même que l'URI pour GET toutes les ressources de ce type, la seule différence étant la méthode HTTP POST.

5. Conclusion

Nous avons vu comment une API REST est entièrement détectable à partir de la racine et sans connaissances préalables - ce qui signifie que le client est capable de la naviguer en effectuant un GET à la racine. À l'avenir, tous les changements d'état sont pilotés par le client à l'aide des transitions disponibles et détectables que l'API REST fournit dans les représentations (d'où le transfert d'état de représentation ).

Cet article a couvert certains des traits de la découvrabilité dans le contexte d'un service Web REST, abordant la découverte de méthode HTTP, la relation entre créer et obtenir, la découverte de l'URI pour obtenir toutes les ressources, etc.

L'implémentation de tous ces exemples et extraits de code est disponible à l'adresse over sur GitHub. Il s'agit d'un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.

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