REST-API-Erkennbarkeit und HATEOAS

Erkennbarkeit der REST-API und HATEOAS

1. Überblick

Dieser Artikel konzentriert sich aufDiscoverability of the REST API, HATEOAS und praktische Szenarien, die von Tests gesteuert werden.

2. Warum die API erkennbar machen?

Die Auffindbarkeit einer API ist ein Thema, das nicht genügend verdiente Aufmerksamkeit erhält. Infolgedessen machen es nur sehr wenige APIs richtig. Es ist auch etwas, das, wenn es richtig gemacht wird, die API nicht nur RESTful und benutzerfreundlich, sondern auch elegant machen kann.

Um die Auffindbarkeit zu verstehen, müssen wir die Einschränkung Hypermedia als Engine Of Application State (HATEOAS) verstehen. This constraint of a REST API is about full discoverability of actions/transitions on a Resource from Hypermedia (Hypertext really), as the only driver of application state.

Wenn die Interaktion von der API durch die Konversation selbst gesteuert werden soll, konkret über Hypertext, kann es keine Dokumentation geben. Dies würde den Kunden dazu zwingen, Annahmen zu treffen, die tatsächlich außerhalb des Kontexts der API liegen.

Zusammenfassend lässt sich sagen, dassthe server should be descriptive enough to instruct the client how to use the APInur über Hypertext erfolgt. Im Falle einer HTTP-Konversation können wir dies durch den HeaderLinkerreichen.

3. Erkennbarkeitsszenarien (angetrieben durch Tests)

Was bedeutet es für einen REST-Service, erkennbar zu sein?

In diesem Abschnitt werden einzelne Erkennbarkeitsmerkmale mit Junit,rest-assured undHamcrest getestet. Dathe REST Service has been previously secured, muss jeder Test zuerstauthenticate ausführen, bevor die API verwendet wird.

3.1. Entdecken Sie die gültigen HTTP-Methoden

Wenn ein REST-Service mit einer ungültigen HTTP-Methode verwendet wird, sollte die Antwort eine 405-Methode sein, die nicht zulässig ist.

Die API sollte dem Client auch dabei helfen, die gültigen HTTP-Methoden zu ermitteln, die für diese bestimmte Ressource zulässig sind. Dazuwe can use the Allow HTTP Header in the response:

@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. Ermitteln Sie den URI der neu erstellten Ressource

The operation of creating a new Resource should always include the URI of the newly created resource in the response. Hierfür können wir den HTTP-HeaderLocation verwenden.

Wenn der Client einen GET für diesen URI ausführt, sollte die Ressource verfügbar sein:

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

Der Test folgt einem einfachen Szenario:creating a new Foo resource, then using the HTTP response to discover the URI where the Resource is now available. Anschließend wird ein GET für diesen URI ausgeführt, um die Ressource abzurufen und mit dem Original zu vergleichen. Dies soll sicherstellen, dass es korrekt gespeichert wurde.

3.3. Ermitteln Sie den URI, um alle Ressourcen dieses Typs abzurufen

When we GET any particular Foo resource, we should be able to discover what we can do next: we can list all the available Foo resources. Daher sollte der Vorgang des Abrufens einer Ressource in seiner Antwort immer den URI enthalten, unter dem alle Ressourcen dieses Typs abgerufen werden können.

Hierfür können wir wieder den HeaderLinkverwenden:

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

Beachten Sie, dass der vollständige Low-Level-Code fürextractURIByRel - verantwortlich für das Extrahieren der URIs nachrel Relationis shown here ist.

Dieser Test behandelt das heikle Thema der Linkbeziehungen in REST: Der URI zum Abrufen aller Ressourcen verwendet die Semantik vonrel=”collection”.

Diese Art der Verknüpfungsbeziehung wurde noch nicht standardisiert, beträgt jedoch bereitsin use von mehreren Mikroformaten und wird zur Standardisierung vorgeschlagen. Die Verwendung von nicht standardmäßigen Verknüpfungsbeziehungen eröffnet die Diskussion über Mikroformate und eine umfassendere Semantik in RESTful-Webdiensten.

4. Andere potenzielle auffindbare URIs und Mikroformate

Other URIs could potentially be discovered via the Link header, aber es gibt nur so viel, was die vorhandenen Arten von Verknüpfungsbeziehungen zulassen, ohne zu einem reichhaltigeren semantischen Markup wiedefining custom link relations,Atom Publishing Protocol odermicroformats überzugehen das Thema eines anderen Artikels.

Beispielsweise sollte der Client in der Lage sein, den URI zum Erstellen neuer Ressourcen zu ermitteln, wenn erGET für eine bestimmte Ressource ausführt. Leider gibt es keine Verknüpfungsbeziehung zur Semantik des Modellscreate.

Glücklicherweise ist es üblich, dass der URI für die Erstellung mit dem URI zum Abrufen aller Ressourcen dieses Typs identisch ist. Der einzige Unterschied besteht in der POST-HTTP-Methode.

5. Fazit

Wir habenhow a REST API is fully discoverable from the root and with no prior knowledge gesehen - was bedeutet, dass der Client in der Lage ist, durch Ausführen eines GET im Stammverzeichnis zu navigieren. In Zukunft werden alle Statusänderungen vom Client mithilfe der verfügbaren und erkennbaren Übergänge gesteuert, die die REST-API in Darstellungen bereitstellt (daherRepresentational State Transfer).

Dieser Artikel befasste sich mit einigen Merkmalen der Erkennbarkeit im Kontext eines REST-Webdienstes und ging auf die HTTP-Methodenerkennung, die Beziehung zwischen create und get, die Erkennung des URI zum Abrufen aller Ressourcen usw. ein.

Die Implementierung all dieser Beispiele und Codefragmente istover on GitHub verfügbar. Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.