Hyperlinks

Korrekte Hyperlinks mit dem Jersey Framework

Wie wir alle wissen, gehört zu einer vollständigen Umsetzung der REST Prinzipien nicht nur die korrekte Verwendung der HTTP Verben und damit der CRUD Operationen, sondern auch die Verwendung von Hyperlinks, damit der Server die Clients innerhalb der Anwendung navigieren kann. Bei der Umsetzung des Hypermedia Prinzips in REST APIs ruft man als Entwickler schnell nach einer Unterstützung durch das verwendete Framework. Die manuelle Erzeugung von korrekten URIs ist aufwändig und fehleranfällig. In diesem Artikel beschreibe ich die aktuellen Möglichkeiten, um Hyperlinks mit Jersey, einem Java Framework für REST APIs, zu erzeugen.

Zunächst muss man die beiden Arten von Hyperlinks unterscheiden. Zum einen benötigen wir eingebettete Hyperlinks in den Repräsentation, um von einer Ressource zu einer anderen navigieren zu können. Zum Beispiel enthält folgende Repräsentation einer Person einen Link auf die Stadt, in der die Person geboren wurde.

{ 
   "firstName" : "Max", 
   "lastName" : "Mustermann", 
   "cityOfBirth" : "http://..../cities/Hamburg" 
}

An dieser Stelle statt eines Hyperlinks nur eine Id zu verwenden, die ein Client dann selbst zu einer URI erweitern muss, würde ein wichtiges Prinzip von REST verletzen.

Zum anderen benötigt man Hyperlinks, die für bestimmte Aktionen verwendet werden. Wir nennen diese Hyperlinks ab jetzt Aktionsrelationen. Mit ihnen wird der Workflow des Clients durch die Anwendung beschrieben. Ein erstes Beispiel sind die Hyperlinks auf die vorherige und die nächste Seite einer Suchergebnisliste. Die Frage hierbei ist, ob solche Links ebenfalls in der Repräsentation enthalten sein sollte oder ob man sie in den HTTP Response Header verschieben kann. Die erste Möglichkeit wirkt meiner Ansicht nach etwas künstlich. Man müsste die eigentlichen Suchergebnisse, die ja ein Array von Repräsentationen sind, durch ein weiteren Bereich ergänzen, in dem diese Links abgelegt werden. In RFC 5988 wird vorgeschlagen, solche Hyperlinks in den Header der Antwort zu verschieben.
Zum Beispiel könnte das so aussehen:

HTTP/1.1 200 OK
Link: ; rel="next"
Content-Type: application/json
Content-Length: 179
Date: Wed, 23 Apr 2014 17:41:42 GMT

Wie erzeugt man nun diese beiden Arten von Links? Fangen wir mit den Aktionsrelationen an. In Jersey arbeitet man zum Beispiel mit der Klasse Response, um die Antwort des Servers zu erzeugen. Normalerweise übergibt man bei einer GET Anfrage das Ergebnis an die statische Methode ok, die eine HTTP Antwort mit Status Code 200 erzeugt. Möchte man nun noch einen Link zum Header hinzufügen, benutzt man einfach die Methode link, die als ersten Parameter die URI und als zweiten Parameter den Relationstyp erwartet. Der Relationstyp ist der Wert des Attributes „rel“ im obigen Header, also zum Beispiel next. Hier ein Beispiel für eine einfache Methode für einen GET Zugriff:

1
2
3
4
5
6
7
8
9
@GET
@Path( „persons )
@Produces( { MediaType.APPLICATION JSON, MediaType.APPLICATION XML } )
public Response getPersons( @Context UriInfo uriInfo, @QueryParam(„offset“) int offset, @QueryParam(„size) int size ) 
{
   List<Person> persons = ... // Laden der Ressourcen 
   URI nextUri = ...
   return Response.ok( persons ).link( nextUri, "next" ).build();
}

Innerhalb der obigen Methode erhalten wir Information zum Anfragekontext über den Parameter uriInfo. Darüber bekommen wir mit der Methode getAbsolutePathBuilder einen UriBuilder, der bereits die URI der Anfrage ohne die Query Parameter enthält. Wir nutzen jetzt Methoden des UriBuilder, um daran wieder die Query Parameter anzuhängen:

1
2
UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder().queryParam(„offset“, „{offset}).queryParam(„size“, „{size});
URI nextUri = uriBuilder.build( newOffset, newSize );

An dieser Stelle machen wir uns die Möglichkeit von Templates zunutze, die wir in der Methode queryParam definieren und in der Methode build entsprechend der vorher definierten Reihenfolge setzen. Wem die Zuordnung der Werte zu den Templates über die Reihenfolge nicht gefällt, kann auch die Method buildFromMap nutzen.

Bei den eingebetteten Hyperlinks könnte man nun natürlich ähnlich vorgehen und sich die Hyperlinks über UriBuilder erzeugen lassen. Allerdings bietet hier Jersey noch ein anderes Verfahren an, nämlich das sogenannte Link Injection.
In der Java Klasse, die eine Ressource repräsentiert, werden Attribute vom Typ Link hinzugefügt. Diese Klasse stammt aus der JSR 311 Spezifikation. Zusätzlich wird dieses neue Attribut noch mit @InjectLink annotiert. Wenn Jersey nun aus einem Objekt dieses Typs die passende Repräsentation in JSON erstellt, werden alle Attribute mit dieser Annotation zur Laufzeit mit einer konkreten URI gesetzt, deren Inhalt sich aus den Parametern der Annotation ergibt. Im wesentlichen müssen zwei Parameter innerhalb der Annotation übergeben werden. Zunächst definiert man mit style die Art der URI, entweder eine relative oder eine absolute. Bei REST sollte man immer absolute URIs verwenden. Der zweite Parameter value definiert nun relativ zur Basis-URI der REST Applikation (die man als Annotation vom Typ @ApplicationPath in der Klasse definiert, die Application erweitert) den Pfad und evtl. die Query Parameter. Zum Beispiel könnte eine Klasse Person folgendes Attribut für einen Link auf sich selbst enthalten:

1
2
@InjectLink(style = Style.ABSOLUTE, value = "persons/${instance.id}" )
private Link me;

Der Name me des Attributes wird innerhalb der Repräsentation verwendet. Sein Wert ergibt sich, wie gesagt, aus der Basis-URI der Applikation mit dem Pfad persons und der Id des Objektes. Der Ausdruck ${instance.id} bedeutet dabei, dass die Instanz, also das Objekt, betrachtet wird und mit id wird auf den Wert des Objektattributes id verwiesen.

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.


*