REST-Services mit Spray

Seite 3: Serialisierung

Inhaltsverzeichnis

In CRUD-Szenarien (Create, Read, Update, Delete) nutzt man häufig Routen mit einer bestimmten Struktur: Ein URI stellt eine Liste von Objekten dar. Hängt man ihm einen Schlüssel an, adressiert er ein einzelnes Element der Liste. Um diese Konstellation in der Spray-Routing-DSL abzubilden, ist mit den Direktiven pathPrefix, pathEnd und path zu arbeiten. Das folgende Beispiel definiert die URIs "/items", um alle Items bei einem GET zu erhalten, und "/items/{id}", mit dem man mit derselben Operation lediglich ein durch einen Schlüssel identifiziertes Objekt bekommt.

val route = pathPrefix("items") {
pathEnd {
get {
complete {
allItems()
}
}
} ~ path(IntNumber) {
id => get {
complete {
findItemById(id)
}
}
}
}

Generell kann man sagen, dass die Routing-DSL gegenüber dem bei JAX-RS verwendeten Vorgehen mit Annotationen den Vorteil hat, dass sich die Routing-Regeln kompakt formulieren lassen und oft an genau einer zentralen Stelle im Code zu finden sind. Bei JAX-RS verteilt sich beispielsweise die Definition einer Pfad-Regel oft über mehrere Klassen und wird dadurch schnell unübersichtlich.

Die bisherigen Beispiele waren einfach gehalten: Requests enthalten außer der URI und gegebenenfalls Headern keine weiteren Informationen, Antworten neben dem Statuscode nur einfache Strings. In realen Anwendungen wird allerdings viel mehr ausgetauscht, wobei JSON als Serialisierungsformat für komplexe Datenstrukturen dient. Aus technischer Sicht geht es als Nächstes um die Frage, wie komplexe Datenstrukturen zu serialisieren und deserialisieren sind.

Spray bietet dazu die Bibliothek spray-httpx, um unterschiedliche Möglichkeiten für die Serialisierung beziehungsweise Deserialisierung umzusetzen. Die einfachste Variante für den Umgang mit JSON ist via json4s. Die in der Bibliothek enthaltene Unterstützung stellt eine passende Serialisierung für alle gängigen Konstrukte zur Verfügung: einfache Datentypen, Collections und Scala Case Classes. Ihre Verwendung ist denkbar einfach: Der Trait Json4Support wird in den Aktor eingewebt und die Variable json4sFormats definiert:

class JsonExample extends Actor 
with HttpService with Json4sSupport {
val json4sFormats = DefaultFormats
...
}

Im obigen Beispiel werden ausschließlich die von json4s bereitgestellten Formatierungen verwendet. Sie sind in den meisten Fällen ausreichend und helfen, Case Classes und Collections korrekt zu serialisieren sowie Datumsangaben ins ISO-8601-Format abzubilden.

In der Regel werden in Scala Case Classes verwendet, um Datenstrukturen zu definieren. Die Klasse

case class TodoItem(id: Int, text: String, dueDate: Date)

beschreibt einen Eintrag in einer To-do-Liste. Wenn man die unveränderten Einstellungen von json4s verwendet, erhält man ohne weiteres Zutun das folgende JSON-Konstrukt als Antwort:

{ 
"id": 1,
"text": "Artikel über Spray schreiben",
"dueDate": "2014-06-01T12:00:00"
}