Rilevabilità API REST e HATEOAS

REST Top

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO

1. Panoramica

Questo articolo si concentrerà sulla rilevabilità dell'API REST, HATEOAS e scenari pratici guidati dai test.

2. Perché rendere l'API rilevabile

La rilevabilità di un'API è un argomento che non riceve abbastanza meritata attenzione. Di conseguenza, pochissime API lo fanno bene. È anche qualcosa che, se fatto correttamente, può rendere l'API non solo RESTful e utilizzabile ma anche elegante.

Per comprendere la rilevabilità, è necessario comprendere il vincolo HATEOAS (Hypermedia As The Engine Of Application State). Questo vincolo di un'API REST riguarda la piena rilevabilità di azioni / transizioni su una risorsa da Hypermedia (in realtà ipertesto), come l'unico driver dello stato dell'applicazione.

Se l'interazione deve essere guidata dall'API attraverso la conversazione stessa, concretamente tramite Hypertext, allora non può esserci documentazione. Ciò costringerebbe il cliente a formulare ipotesi che in realtà sono al di fuori del contesto dell'API.

In conclusione, il server dovrebbe essere sufficientemente descrittivo da istruire il client su come utilizzare l'API solo tramite ipertesto. Nel caso di una conversazione HTTP, potremmo ottenere ciò tramite l' intestazione del collegamento .

3. Scenari di rilevabilità (guidati da test)

Quindi cosa significa per un servizio REST essere individuabile?

In questa sezione, testeremo i singoli tratti di rilevabilità utilizzando Junit, tranquilli e Hamcrest. Poiché il servizio REST è stato precedentemente protetto, ogni test deve prima autenticarsi prima di utilizzare l'API.

3.1. Scopri i metodi HTTP validi

Quando un servizio REST viene utilizzato con un metodo HTTP non valido, la risposta deve essere un metodo 405 NON AMMESSO.

L'API dovrebbe anche aiutare il client a scoprire i metodi HTTP validi consentiti per quella particolare risorsa. Per questo, possiamo utilizzare il Consenti intestazione HTTP nella risposta:

@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. Scopri l'URI della risorsa appena creata

L'operazione di creazione di una nuova risorsa dovrebbe sempre includere l'URI della risorsa appena creata nella risposta. Per questo, possiamo utilizzare l' intestazione HTTP della posizione .

Ora, se il client esegue un GET su quell'URI, la risorsa dovrebbe essere disponibile:

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

Il test segue uno scenario semplice: creare una nuova risorsa Foo , quindi utilizzare la risposta HTTP per scoprire l'URI in cui la risorsa è ora disponibile . Quindi esegue anche un GET su quell'URI per recuperare la risorsa e confrontarla con l'originale. Questo per assicurarsi che sia stato salvato correttamente.

3.3. Scopri l'URI per OTTENERE tutte le risorse di quel tipo

Quando OTTENIAMO una particolare risorsa Foo , dovremmo essere in grado di scoprire cosa possiamo fare dopo: possiamo elencare tutte le risorse Foo disponibili. Pertanto, l'operazione di recupero di una risorsa dovrebbe sempre includere nella sua risposta l'URI da cui ottenere tutte le risorse di quel tipo.

Per questo, possiamo ancora utilizzare l' intestazione 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)); }

Si noti che il codice completo di basso livello per extractURIByRel , responsabile dell'estrazione degli URI per relazione rel , è mostrato qui.

Questo test copre l'argomento spinoso delle relazioni di collegamento in REST: l'URI per recuperare tutte le risorse utilizza la semantica rel = "collection" .

Questo tipo di relazione di collegamento non è stato ancora standardizzato, ma è già utilizzato da diversi microformati e proposto per la standardizzazione. L'utilizzo di relazioni di collegamento non standard apre la discussione sui microformati e sulla semantica più ricca nei servizi Web RESTful.

4. Altri potenziali URI rilevabili e microformati

Altri URI potrebbero essere scoperti tramite l' intestazione del collegamento , ma i tipi esistenti di relazioni di collegamento consentono solo così tanto senza passare a un markup semantico più ricco come la definizione di relazioni di collegamento personalizzate, Atom Publishing Protocol o microformati, che sarà l'argomento di un altro articolo.

Ad esempio, il client dovrebbe essere in grado di scoprire l'URI per creare nuove risorse quando esegue un GET su una risorsa specifica. Sfortunatamente, non esiste una relazione di collegamento con la semantica di creazione del modello .

Fortunatamente è una pratica standard che l'URI per la creazione sia lo stesso dell'URI per OTTENERE tutte le risorse di quel tipo, con l'unica differenza che è il metodo POST HTTP.

5. conclusione

Abbiamo visto come un'API REST sia completamente rilevabile dalla radice e senza alcuna conoscenza preliminare , il che significa che il client è in grado di navigare eseguendo un GET sulla radice. Andando avanti, tutte le modifiche di stato sono guidate dal client utilizzando le transizioni disponibili e rilevabili che l'API REST fornisce nelle rappresentazioni (da qui il trasferimento di stato rappresentativo ).

Questo articolo ha trattato alcuni dei tratti della rilevabilità nel contesto di un servizio Web REST, discutendo la scoperta del metodo HTTP, la relazione tra create e get, la scoperta dell'URI per ottenere tutte le risorse, ecc.

L'implementazione di tutti questi esempi e frammenti di codice è disponibile su GitHub. Questo è un progetto basato su Maven, quindi dovrebbe essere facile da importare ed eseguire così com'è.

REST fondo

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO