© Droidworker/Shutterstock.com
RESTful APIs mit OData

The best Way to REST


RESTful APIs sind in aller Munde, doch leider sind damit Fragen der Objektadressierung, Suche, Filterung oder die Abbildung eines Objekts noch nicht geklärt. All das und andere Best Practices für das Bilden von REST-APIs hat uns der OData-Standard abgenommen. Dank der Metadaten, die ein Service mit seinem Modell beschreibt, ist es auch für Entwickler einfacher, den Service zu verstehen und damit erfolgreich zu arbeiten.

Für die Kommunikation zwischen Maschinen gibt es verschiedene Protokolle, wie z. B. Web Services, RPC und andere. Gerade Web Services mit dem SOAP-Protokoll und den WSDL-Dateien gelten eher als schwerfällig und passen nicht in die moderne Welt von Webapplikationen. Da wird eher das REST-Protokoll verwendet, oft auch „leichtgewichtige“ Web Services genannt – was aber aus meiner Sicht nicht ganz zu vergleichen ist: SOAP ist eine RPC-Middleware, mit der komplexe Protokolle gebaut werden können und generell an einen Endpunkt adressiert wird. Bei REST-Protokollen hingegen erfolgt eine direkte Adressierung der Objekte. Es setzt auf dem HTTP-­Protokoll auf und verwendet folglich dessen meistverwendete Methoden GET, POST, DELETE und PATCH/PUT. Die Grundideen vom REST-Protokoll sind:

  • Unabhängigkeit von der Plattform: Es ist also egal, ob z. B. Linux- oder Windows-Systeme miteinander kommunizieren.

  • Unabhängigkeit von der Programmiersprache: Deshalb werden in der Regel JSON- oder XML-Nachrichten verwendet.

  • Basierend auf dem HTTP-Protokoll: Es ist somit cachebar und es sind standardisierte Regelwerke für Firewalls möglich.

  • Zustandslosigkeit: Jede Nachricht enthält alle notwendigen Informationen für eine erfolgreiche Verarbeitung.

Um sich einen REST-Aufruf vorstellen zu können, kann man z. B. mit folgendem HTTP Request alle Orders (Bestellungen) anzeigen: GET /SalesOrderSet HTTP/1.1. Als Antwort könnte etwas wie in Listing 1 gezeigt zurückkommen.

Listing 1

{ "d" : { "results" : [ { "CreationDateTime" : "\/Date(1481065200000+0000)\/", "Customer" : "100000005", "GrossAmountInTransacCurrency" : "5631.080", "LastChangedDateTime" : "\/Date(1481065200000+0000)\/", "NetAmountInTransactionCurrency" : "4732.000", "SalesOrder" : "500004522", "TaxAmountInTransactionCurrency" : "899.080", "TransactionCurrency" : "EUR", "SalesOrder_Text" : "Text zu Salesorder 0500004522" ... } ] } }

Soweit ist das ja ganz gut, eine Bestellung dürfte aber einen Kunden haben, eine Lieferadresse, Positionen usw. Also müsste man als Nächstes die Details zu solch einer Bestellung abfragen. Weiter wäre es spannend, die Relationen zu den anderen Objekten adressieren zu können, also z. B. „Gib mir die Details zum Kunden der Bestellung“. Klar, diese Datenmodellierung ist unsere Aufgabe als Entwickler. Auch ist es unsere Aufgabe, die URI-Definition zur Verfügung zu stellen und den Content so zurückzugegeben, dass sich eine Bibliothek durch das Datenmodell hangeln kann. Dazu gibt es verschiedene Hilfsmittel und Methoden; eine davon ist OData – und diese möchte ich nachfolgend genauer vorstellen.

Was ist OData?

OData [1] (Open Data Protocol) ist ein OASIS-Standard und folglich Open Source. Auf der Homepage selbst wird es als „the best way to REST“ angepriesen. Ich überlasse es Ihnen, zu entscheiden, ob es die beste Art und Weise ist – ich finde die Weise jedenfalls sehr angenehm. Die Idee hinter OData ist, dass sich der Entwickler auf die Businesslogik konzentrieren kann und sich nicht um den Protokolloverhead kümmern muss.

Den Vorsitz der OData-Standardisierung hat je ein Mitarbeiter von Microsoft und SAP. Verschiedene weitere Unternehmen, wie z. B. IBM, Progress Software oder Northeastern University, lassen ihre Mitarbeiter und Mitarbeiterinnen daran arbeiten. Die aktuelle Version ist Version 4; sie wurde bereits 2013 publiziert. Mit dieser Version ist nicht mehr das ATOM-Format (XML), sondern JSON das Standardformat der Nachricht. Es wurde dafür auch stark überarbeitet.

Grundlagen

OData stellt verschiedene Demoservices für das Ausprobieren und Spielen frei zugänglich zur Verfügung. Wir verwenden in den folgenden Beispielen den öffentlichen Demoservice http://services.odata.org/V4/OData/OData.svc, so können Sie die Beispiele einfach nachvollziehen. Aber welche Funktionalität bietet uns dieser Service? Rufen Sie einfach diesen URL auf, als Antwort erhalten Sie eine Liste mit den möglichen Collections (Listing 2).

Listing 2

<service xmlns=http://www.w3.org/2007/app xmlns:atom=http://www.w3.org/2005/Atom xml:base="http://services.odata.org/V3/OData/OData.svc/"> <workspace> <atom:title>Default</atom:title> <collection href="Products"> <atom:title>Products</atom:title> </collection> <collection href="ProductDetails"> <atom:title>ProductDetails</atom:title> </collection> <collection href="Categories"> <atom:title>Categories</atom:title> </collection> <collection href="Suppliers"> <atom:title>Suppliers</atom:title> </collection> … </workspace> </service>

Daraus erkennen wir, dass es Produkte, Produktkategorien usw. gibt. Aber viel weiter hilft uns das nicht – und erst recht nicht, wenn wir diese Informationen mit einem Tool interpretieren wollen. Um alle Details zu erhalten, muss der Service mit dem URL-Parameter $metadata aufgerufen werden – z. B. im Browser:

http://services.odata.org/V4/OData/OData.svc/$metadata

Als Antwort erhalten wir eine XML-Beschreibung für den Service. Ab der vierten Zeile in Listing 3 finden wir die Beschreibung des Produkts.

Listing 3

<EntityType Name="Product"> <Key> <PropertyRef Name="ID"/> </Key> <Property Name="ID" Type="Edm.Int32" Nullable="false"/> <Property Name="Name" Type="Edm.String"/> <Property Name="Description" Type="Edm.String"/> <Property Name="ReleaseDate" Type="Edm.DateTime" Nullable="false"/> <Property Name="DiscontinuedDate" Type="Edm.DateTime"/> <Property Name="Rating" Type="Edm.Int16" Nullable="false"/> <Property Name="Price" Type="Edm.Double" Nullable="false"/> <NavigationProperty Name="Categories" Type="Collection(ODataDemo.Category)" Partner="Products" /> <NavigationProperty Name="Supplier" Type="ODataDemo.Supplier" Partner="Products" /> <NavigationProperty Name="ProductDetail" Type="ODataDemo.ProductDetail" Partner="Product" /> </EntityType>

Eine Entität (EntityType) ist ein Objekt, z. B. ein Produkt, eine Bestellung, ein Foto usw. Eine Übersicht finden Sie in Tabelle 1. Dann gibt es EntitySets, das sind Arrays/Collections von Entitäten. Betrachten wir in Abbildung 1 stellvertretend die Stellen, die etwas mit dem Produkt zu tun haben.

friberg_odata_1.tif_fmt1.jpgAbb. 1: OData-Modell, Bereiche der Produkte

Wir erkennen, dass ein Produkt (Product) eine oder mehrere Kategorien (Category) hat. Weiter hat ein Produkt Details (ProductDetail). In der Realität würde diese Entität wohl viel mehr Attribute enthalten. Zu guter Letzt gibt es vielleicht zu einem Produkt noch maximal einen Supplier.

OData-Tools

Wer gerne OData in Eclipse betrachten will, analog dem Printscreen, kann z. B. Eclipse Ogee [2] verwenden. Ein weiteres hilfreiches Tool für das Absetzen und Spielen mit den OData-Anfragen ist Postman [3]

Betrachten wir für die ersten Versuche die Entität Product von oben. Eine wichtige Information für die direkte Adressierung eines Produkts ist das Schlüsselattribut ID. Als Datentyp werden verschiedene EDM-Typen unterstützt [4], wobei Angaben wie MaxLength in Edm.String möglich sind. Weitere Angaben wie nullable drücken aus, ob das Feld leer sein darf. Verschiedene OData-Implementationen reichern die Felddefinitionen um weitere Attribute an, z. B. SAP mit sap:filterable, sap.sortable … Somit wissen wir, ob der Service-Implementator z. B. auch einen Filter implementiert hat.

Wieder zurück zum Standard: Interessant dürfte die Navigation sein. Das letzte NavigationProperty sagt aus, dass es eine Entität ProductDetail gibt. Das Ziel dieser Metadaten ist, dass die Schnittstelle möglichst komplett beschrieben und sowohl von Mensch als auch Maschine lesbar ist. In unseren Projekten bestätigte sich das – und wir hatten beim Kaffee wieder mehr Zeit für die wesentlichen Themen.

Parameter

Bedeutung

$metadata

Metainformationen des Service

$count

Anzahl der Entitäten

$top $skip

Paging

$expand

Weitere Entitäten einer Relation mitselektieren

$orderby

Sortierung von Entitäten

$filter

Filter von Entitäten

$search

Beliebi...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang