Rest – HATEOAS + HAL

REST-Webservices sind wohl die am weitesten verbreitete Schnittstellentechnologie die im WEB zu finden ist. Dank JSON als Datenformat bietet REST eine zustandslose und leichtgewichtige Möglichkeit Daten zwischen Anwendungen aus zu tauschen. HATEOAS (Hypermedia as the Engine of Application State) ist nun im Grunde keine weitere Technologie, sondern ein Konzept wie eine REST-Schnittstelle aufgebaut werden sollte.

Der Grundgedanke hinter HATEOAS ist das Clients in der Lage sind die Funktionen der Anwendungs-API zu verwenden ohne das diese fest verdrahtet im Client-Code sein müssen. Vergleichbar ist dies mit der menschlichen Nutzung einer Webseite. Wir besuchen einen Internetauftritt, erhalten Informationen, aber auch weiterführende Links über die wir weitere Funktionen/Seiten abrufen können. Das Prinzip lässt sich nahezu eins zu eins auf HATEOAS anwenden. Anstatt lediglich die angeforderten Informationen in der Datenstruktur von JSON zu erhalten informiert uns die Anwendung ebenfalls über weiterführende Aktionen und Links.

Ein einfaches Beispiel ist z.B. die Abbildung von JPA Entitäten im JSON Format. Nehmen wir ein sehr einfaches Datenmodell aus zwei Entitäten: User und Message. Die Entitäten besitzen eine Bi-Direktionale Verbindung:

@Entity
public class Message {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  private Date date;

  private String message;

  @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST })
  private User user;

------------------------------------------------------------------------

@Entity
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  private String username;

  @OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
  private List<Message> messages;

Bereits hier müssen wir uns Gedanken über die Umwandlung in ein JSON Format machen: ohne zusätzlichen Eingriff von unserer Seite könnten diese Entitäten bereits nicht in ein JSON Format umgewandelt werden, da die Bi-Direktionale Relation zu einer Endlosschleife bei der Umwandlung führen würde. Neben die Bildung von DTOs richten sich die Möglichkeiten um dieses Problem zu beheben nach dem verwendeten JSON Parser (Jackson bietet z.B. Annotationen an um Felder zu ignorieren, Sub-Views aus zu liefern,  solche Relationen zu kennzeichnen oder diese lediglich mit der entsprechenden ID aus zu liefern). Eine klassischer JSON Output für unser „Message“-Objekt könnte nach diesem Prinzip so aussehen:

{
     "date": "1970-01-01T00:00:00.000+0000",
     "message": "Hello World",
     "user": 2
}

Anstatt das Userobjekt mit aus zu liefern bietet das JSON hier nur die entsprechende ID an. Wenn der Client auch die Benutzer-Information benötigt muss dieser wissen das es sich bei dieser ID um eine User-ID handelt, zudem muss die entsprechende URL zum Abruf der User-Daten (z.B. “ http://…/user/2 „) im Client-Code festgelegt sein.

HAL

Zur Unterscheidung: HATEOAS ist der Architektur-Ansatz, HAL ist eine Spezifikation zur JSON-Syntax der Link-Relationen. Neben HAL gibt es noch einige Alternativen (Collection+JSON, Hydra…). HAL findet z.B. Verwendung in „Spring HATEOAS“. Mittels „HAL“ können die Informationen nun verlinkt werden und der Client damit in die Lage versetzt werden die benötigen zusätzlichen Informationen ab zu rufen, ohne die konkreten URLS selber zusammen zu setzen.

Unser Message-Objekt könnte mittels „HAL“ dann wie folgt aussehen:

{
    "date": "1970-01-01T00:00:00.000+0000",
    "message": "Hello World",
    "_links": {
        "self": {
            "href": "http://localhost:8080/msg/2"
        },
        "user": {
            "href": "http://localhost:8080/msg/2/user"
        }
    }
}

Header Links

Eine andere Möglichkeit besteht darin Links im HTTP Header zu verankert. Dies lässt sich auch mit JAX-RS direkt erledigen. Ein einfaches Beispiel um innerhalb der Rest-Methode einen Link der Response hinzuzufügen könnte so aussehen:

  @Context
  UriInfo uriInfo;

  @GET
  @Path("hal")
  public Response getHelloHAL() {
    DemoEntity demo = new DemoEntity(1, "Hello World");
    return Response.ok(demo).link(uriInfo.getBaseUri().resolve("login"), "login").build();
  }

Der Header wird dann um einen entsprechenden Eintrag erweitert und enthält den angegebenen Link:

...
link <http://localhost:8080/angular-jwt-1.0-SNAPSHOT/rest/login>; rel="login"
...

Im nächsten Artikel werfen wir einen Blick auf Spring-Boot und dessen Möglichkeiten HATEOAS und HAL mit sehr wenig Aufwand in der eigenen Anwendung zu verwenden.

Advertisements

Über Dominik Mathmann
Dominik Mathmann arbeitet als Berater, Entwickler und Trainer bei der GEDOPLAN GmbH. Auf Basis seiner langjährigen Erfahrung in der Implementierung von Java-EE-Anwendungen leitet er Seminare, hält Vorträge und unterstützt Kunden bei der Konzeption und Realisierung von Webanwendungen vom Backend bis zum Frontend. Sein derzeitiger Schwerpunkt liegt dabei auf der Entwicklung von JSF-Anwendungen. Er sieht die stärker werdende Position von JavaScript-Frameworks jedoch positiv und beschäftigt sich darüber hinaus mit Webframeworks wie AngularJS.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: