Heute will ich euch vormachen, wie ihr einen ODATA Service anlegen und testen könnt.
Dabei erkläre ich zuerst einmal das ODATA Model mit Entitätstypen und EDM-Typen. Danach zeige ich euch, wie ihr alle 5 Standard-Methoden des ODATA Services implementieren und testen könnt. Ich zeige euch hierbei mein Best-Practice vorgehen zum Arbeiten mit ODATA und weise gelegentlich auf weitere Alternativen hin.
ODATA Model
Das ODATA Model bildet das Daten Modell eines ODATA Services ab. Der ein oder andere kennt den Begriff „Entität“ sicherlich noch aus alten Datenbankvorlesungen. Falls nicht, auch nicht schlimm. Das Ganze ist schnell erklärt: Einen Entitätstypen könnt ihr euch wie eine ABAP Struktur vorstellen. Ihr definiert Felder mit den entsprechenden Datentypen und einigen weiteren Eigenschaften. Allerdings habt ihr hierfür nicht die ABAP Datentypen zur Verfügung.
ODATA ist eine Schnittstellentechnologie, welche nicht nur von SAP verwendet wird. Daher gelten hier die EDM-Datentypen. In den meisten Fällen gibt es im entsprechenden SAP Backend auch eine Struktur mit genau den selben Feldern und SAP Datentypen, die dem Entitätstypen gleicht. Aber genug der Theorie, nun zeige ich euch, wie ihr euren ersten Entitätstypen anlegen könnt.
Entitätstyp anlegen
Es gibt verschiedene Möglichkeiten, einen Entitätstypen anzulegen. Ihr könnt die Felder beispielsweise manuell einpflegen und mappen, oder direkt aus einer entsprechenden SAP Struktur importieren. Ich erspare mir gerne die Arbeit und importiere die Felder. Dafür muss ich mir also zunächst eine entsprechende ABAP Struktur in der Transaktion SE11 anlegen:
![]()
Für mein Beispiel will ich eine Fiori Anwendung schreiben, welche mit Personen arbeitet. Dafür habe ich mir eine Struktur angelegt, welche eine Personalnummer, verschiedene Namensfelder und das Geburtsdatum enthält. Auf Basis dieser Struktur will ich jetzt einen Entitätstypen anlegen. Dafür gehe ich in die Transaktion SEGW und lege mir erst einmal ein ODATA Projekt an.
![ODATA Projekt anlegen]()
Ihr solltet nun ein neues ODATA Projekt mit 4 automatisch generierten Unterordnern vor euch haben. Mit Rechtsklick auf „Data Model“ bekommt ihr nun die Optionen, einen neuen Entitätstypen frei anzulegen oder aus einer ABAP-Dictionary-Struktur zu importieren:
![]()
Zuerst geben wir den Namen eures Entitätstypen an und die eben angelegte Dictionary-Struktur, aus der wir die Felder importieren möchten:
![Entitätstyp anlegen Assistent 1]()
Im Anschluss legen wir fest, welche Felder aus der Struktur mit in den ODATA Service übernommen werden sollen:
![Entitätstyp anlegen Assistent 2]()
Als letztes legen wir den Schlüssel für die Entität fest. Dieser Schritt wird später großen Einfluss haben. Wichtig ist, dass später jedes Entity eindeutig über den Schlüssel identifiziert werden kann. Ich versuche immer, dafür mit so wenigen Feldern wie möglich auszukommen, um es mir später nicht unnötig kompliziert zu machen. In diesem Falle reicht mir die Personalnummer aus, um jede Person eindeutig zu identifizieren:
![Entitätstyp anlegen Assistent 3]()
Unseren ersten Entitätstypen haben wir hiermit bereits angelegt. Er sollte nun im Ordner „Data Model“ unter „Entitätstypen“ sichtbar sein. Unter eurem Entitätstypen sehr ihr nun Eigenschaften und Navigationseigenschaften. Für uns interessant sind erst einmal die Eigenschaften. Mit einem Doppelklick können wir uns ansehen, welche Felder mit welchen Entitätstypen wir im Entitätstypen haben, welche Eigenschaften dazu gehören und zu welchem Feld unserer Struktur diese gemapped sind. Das Mapping von ABAP-Datentyp zu EDM-Typ wurde beim Erstellen automatisch vorgenommen.
![Entitätstyp Eigenschaften]()
EDM-Typen
Wie ihr sehen könnt, weichen die EDM-Typen etwas von den ABAP-Typen ab. Mit F4 (Suchhilfe) auf eines der EDM-Coretyp Felder, könnt ihr euch alle vorhandenen EDM-Typen anzeigen lassen:
![Übersicht EDM-Typen]()
In sehr vielen Fällen wird der ABAP Datentyp auf den Edm.String Typen abgebildet. Häufig passt das auch gut für die Datenübertragung. Allerdings habe ich festgestellt, das zum Beispiel der ABAP Boolean als Char1 auch als String und nicht als Edm.Boolean abgebildet wird. Das korrigiere ich immer nachträglich, ansonsten benötigt ihr ein zusätzliches Mapping sowohl im Frontend, als auch im Backend, um mit dem Feld weiterarbeiten zu können.
Auch bei der Übertragung von Daten solltet ihr aufpassen. ODATA hat keinen Datentyp für ein normales Datum. Es gibt nur den Typen Edm.DateTime, welcher neben dem Datum auch noch die Uhrzeit und die Zeitzone überträgt. Übertragt ihr ein normales Datum aus dem Backend, werden die fehlenden Informationen einfach standardmäßig aufgefüllt. Im Frontend müsst ihr das Datum dann wieder in das gewünschte Format bringen und die zusätzlichen Informationen wieder rausfiltern. Bei der Übertragung vom Frontend ans Backend kann es aufgrund der Zeitzonen zu einer Verschiebung des Datums um einen Tag kommen. Das sollte dann im Besten Fall direkt im Frontend vor der Datenübertragung schon korrigiert werden. Bei Daten ist es auch wichtig, den Haken bei der Eigenschaft „Nullwerte möglich“ zu setzen. Ansonsten müsst ihr zwangsläufig immer einen Wert mitliefern.
Nachdem alle Eigenschaften korrigiert sind, klickt ihr oben auf den Button „Laufzeitobjekte generieren“. Dieser prüft das Projekt auf Konsistenz und erzeugt die für die Datenübertragung benötigten Klassen mit den entsprechenden Schnittstellen:
![Laufzeitobjekte erzeugen]()
Service registrieren
Euer Service ist im Backend nun angelegt, kann aber von außen noch nicht gesehen werden. Um dies zu ermöglichen, müssen wir ihn noch registrieren. Hierfür gibt es wieder verschiedene Möglichkeiten. Normalerweise wird der Service entweder in der Sap Cloud Plattform (SCP) registriert, oder auf einem Gateway-System. Die Services direkt auf dem Backend-SAP System zu registrieren funktioniert auch, wird meist aber aus Sicherheitsgründen nur zu Testzwecken genutzt. Ich werde die Services im Folgenden auf einem extra Gateway System registrieren.
Also melde ich mich entsprechend am Gateway System an und starte die Transaktion /n/IWFND/MAINT_SERVICE . Dort füge ich den Service über die entsprechende Schaltfläche hinzu:
![ODATA Service hinzufügen]()
Im ersten Schritt wähle ich nun das Backend-System aus, auf dem ich meinen ODATA Service implementiert habe:
![System auswählen]()
Sollte euer System nicht in der Suchhilfe zu finden sein, sind noch Konfigurationen am System notwendig. Darauf werde ich in diesem Tutorial nicht näher eingehen. Nachdem ihr euer System ausgewählt habt, sucht ihr in der Liste euren Service und fügt diesen dann hinzu. Im folgenden Fenster spezifiziert ihr noch das Entwicklungspaket, schon ist der Service erfolgreich registriert:
![Service hinzufügen]()
ODATA Services
Wir haben jetzt unseren Entitätstypen angelegt und die Eigenschaften der Felder entsprechend unserer Wünsche angepasst. Nun wird es Zeit, die Datenübertragung zu ermöglichen. Dafür gibt es die bereits erwähnten 5 Standard-Methoden. Im folgenden werden wir alle 5 Methoden kurz besprechen und für unser Beispiel kurz implementieren. Dafür doppelklicken wir den Ordner „Laufzeitartefakte“ im ODATA Projekt (auf dem Backend-System) und öffnen die ABAP-Klasse, welche auf „DPC_EXT“ endet. Ganz wichtig ist hierbei, dass ihr diese Klasse nehmt. In der Klasse, welche auf „DPC“ endet findet ihr die selben Methoden. Eure Änderungen werden hier aber jedes Mal komplett überschrieben, wenn ihr die Laufzeitobjekte neu generiert nach einer Änderung am Datenmodell. Die „DPC_EXT“ Klasse ist eine Erweiterung der „DPC“ Klasse. Daher habt ihr hier die Möglichkeit, alle geerbten Methoden zu redefinieren.
![DPC_EXT Klasse]()
Die 5 Service-Implementierungen werden aus der Klasse …_DPC geerbt und werden entsprechend in der Methoden-Übersicht der Klasse aufgeführt:
![Service-Implementierungen]()
GET_ENTITYSET
GET_ENTITYSET liefert eine Menge von Entitäten zurück. Dies wird zum Beispiel häufig verwendet, um eine Liste von Elementen zu befüllen. In unserem Fall der SplitApp verwenden wir den Service dafür, die Masterliste auf der linken Seite des Bildschirms zu befüllen. Hierfür rufen wir den Service aus dem Frontend ohne Filter auf und steuern im Backend, welcher Benutzer welche Personen sehen soll.
Nachdem wir in den Änderungsmodus gewechselt sind, können wir die Methode GET_ENTITYSET für unseren Entitätstypen redefinieren. Dadurch legen wir in der Klasse DPC_EXT eine eigene Implementierung zu der geerbten Methode an:
![Service-Implementierung redefinieren]()
Innerhalb dieser Methode können wir nun die vom Frontend übergebenen Filter auslesen, die Anfragen im Backend verarbeiten und die Ergebnisse wieder in die Schnittstelle füllen. Da in unserem Fall kein Filter benötigt wird, sparen wir uns das an dieser Stelle. Aber keine Sorge, auf die Filter werde ich in den nächsten Services noch eingehen. Für die Methode GET_ENTITYSET steht die Tabelle et_entityset als Container für die Ergebnisdaten zur Verfügung. Diese müssen wir also in unserer Methode befüllen.
Ich habe mir eine eigene Klasse für den Entitätstypen angelegt, welche ich zum Befüllen der Schnittstelle verwenden werde. Theoretisch könnt ihr den Quellcode natürlich einfach in die Methode GET_ENTITYSET implementieren, aus meiner Sicht ist eine Trennung an dieser Stelle allerdings sauberer. Die Tabelle et_entityset basiert auf dem Entitätstypen, welcher wiederum auf unserer ursprünglich angelegten Struktur basiert. Wir können also einfach einen Tabellentyp dieser Struktur als Rückgabe für unsere Methode wählen und das et_entityset somit direkt befüllen. Sollten wir nicht alle Felder aus der Struktur übernommen haben, oder weitere Felder zum Entitätstypen hinzugefügt haben, wäre an dieser Stelle noch ein Mapping notwendig.
![GET_ENTITYSET]()
Unsere erste Serviceimplementierung ist nun fertig gestellt. Aber funktioniert das auch alles so, wie wir uns das vorstellen? Wir wollen unseren neuen Service natürlich direkt testen, bevor wir das Frontend dazu fertig gestellt haben. Hierfür loggen wir uns wieder im Gateway System ein und starten die Transaktion /n/iwfnd/maint_service. Hier haben wir die Möglichkeit, den Service über den SAP Gateway Client zu testen:
![SAP Gateway Client]()
Hier ist automatisch die HTTP Methode „GET“ und die zu unserem ODATA Service passende Request-URI voreingestellt. Den ersten Test starten wir, indem wir die vorhinterlegten Einstellungen ausführen. Hat bis hierhin alles gut funktioniert, sollten wir den Status Code 200 OK in einem grün hinterlegten Fenster sehen. Zusätzlich steht im Fenster rechts unten die Antwort, welche alle von uns hinterlegten Entitätstypen enthält:
![Service Metadate]()
Sollte das bei euch nicht funktioniert haben, probiert mal die Transaktion /n/iwfnd/error_log aus. Hier findet ihr meist hilfreiche Debugging-Informationen, wenn ODATA Aufrufe fehlgeschlagen sind.
Um den gerade implementierten Service GET_ENTITYSET zu testen, müssen wir lediglich die URL hinter dem _SRV abändern. Hier tragen wir einfach das Set des eben von uns erstellten Entitätstypen ein: PersonSet
Hier bekomme ich nun für jede Person, die meine Methode GET_ENTITYSET zurückliefert, einen Eintrag:
![Test GET_ENTITYSET]()
Falls ihr hierbei nicht den Code 200, sondern einen Fehler bekommt, empfehle ich 2 Möglichkeiten zum Debugging. Ihr könnt direkt am Anfang der Methode GET_ENTITYSET einen externen Breakpoint setzen. Solltet ihr erst gar nicht bis dahin kommen, hilft wieder die Transaktion /n/iwfnd/error_log auf dem Gateway System. Sobald bei euch auch der grüne Statuscode und die richtigen Personen im Ergebnis stehen, habt ihr es geschafft! Unser GET_ENTITYSET funktioniert!
GET_ENTITY
GET_ENTITY liefert als Ergebnis ein einziges, spezielles Entity anstelle einer Ergebnismenge. Bei diesem Service muss der eindeutige Schlüssel mitgegeben werden, um das Entity zu identifizieren. Um den Service zu implementieren, redefinieren wie die Methode GET_ENTITY aus unsere DPC_EXT Klasse analog zum vorigen Vorgehen.
Innerhalb der Methode müssen wir zuerst die mitgegebene ID auslesen. Die regulär übergebene ID ist in der Tabelle IT_KEYTAB als Key-Value-Pair zu finden. Der Schlüssel ist hierbei die Bezeichnung des jeweiligen Attributs. In unserem Falle „Pernr“, da wir dies als Schlüssel unseres Entitätstypen definiert haben. Der entsprechende Wert dazu steht dann im „Value“ Attribut des Tabelleneintrages. Ich lese die PerNr in diesem Falle wie folgt aus:
![GET_ENTITYSET]()
Die Ergebnismenge aus dem Backend wird am Ende in den Export-Parameter er_entity geschrieben. Hierbei handelt es sich um eine Struktur vom Typ des ODATA Entitätstypen. Für die Backend-Logik ist es an euch, den passenden Personensatz zur übergebenen Personalnummer aufzubereiten.
Zum Testen der Implementierung gehe ich wieder in den SAP Gateway Client auf meinem Gateway-System und passe die URL etwas an, sodass ich die entsprechende URL übergebe zu meinem Personenset: /sap/opu/odata/sap/ZFTENT_HOWTO_ODATA_SRV/PersonSet(‚12345678‘). Wenn alles fehlerfrei ist, bekomme ich den grünen Code 200 zurück und in dem xml-Dokument die Details zu meiner Person:
![Ergebnis GET_ENTITY]()
CREATE_ENTITY
Create Entity soll einen neuen Datensatz im Backend anlegen. Dafür müssen vom Frontend natürlich alle relevanten Eigenschaften mitgegeben werden. Um den Service zu implementieren, redefinieren wir die Methode CREATE_ENTITY der DPC_EXT Klasse analog zu den vorigen Malen. Im der Methode suchen wir diesmal nicht nach einem Key, sondern nach allen relevanten Informationen, um den neuen Datensatz anzulegen. Dafür wird uns das Objekt io_data_provider mitgeliefert. Mit der Methode read_entry_data() bekommen wir alle vom Frontend übergebenen Daten zurück.
Für den Export-Parameter er_entity müssen wir eine Struktur vom Typ des ODATA Entitätstypen mitgeben. Die ausgelesenen Daten können wir nun im Backend verwenden, um einen entsprechenden Datensatz zu erstellen. Wichtig hierbei ist, dass wir den Export-Parameter er_entity vor Ende der Methode befüllen, da ansonsten der HTTP Fehlercode 500 zurückgegeben wird. Normalerweise werden einige Eigenschaften im Backend generiert und können nach Erstellung dann an das Frontend zurückgegeben werden. Da dies bei mir nicht der Fall ist, übergebe ich einfach die selben Daten wieder zurück:
![CREATE_ENTITY]()
Zum Testen müsst ihr dementsprechend natürlich Informationen für den zu erstellenden Backend-Datensatz mitgeben. Im Gateway-Client könnt ihr euch dafür einfach behelfen: Verwendet erst den vorhin beschriebenen GET_ENTITY Aufruf, um einen Datensatz im rechten xml Ergebnisfeld zu haben. Dann könnt ihr einfach rechts oben in der Leiste auf „Als Anf. verwenden“ klicken, um die xml Struktur im linken Feld zu sehen:
![Resp. als Anf. verwenden]()
In der xml-Struktur auf der linken Seite löscht ihr nun alles zwischen dem <entry> und dem <content> Tag. Jetzt sollte der verbleibende Teil etwa so aussehen:
![Anzupassender xml Datensatz]()
Den verbliebenen Datensatz könnt ihr anpassen, sodass er eurem neu einzufügendem Satz entspricht. Nun müssen wir nur noch die HTTP auf „Post“ stellen und die selbe URL wie für den Test vom GET_ENTITYSET verwenden: /sap/opu/odata/sap/ZFTENT_HOWTO_ODATA_SRV/PersonSet
Wenn alles funktioniert habt, solltet ihr jetzt den grünen HTTP Statuscode 201 – Created sehen und den zurückgelieferten Datensatz im Ergebnis auf der rechten Seite:
![Entity angelegt 201]()
UPDATE_ENTITY
Für die UPDATE_ENTITY Methode übergeben wir sowohl einen Schlüssel zur Identifizierung des Datensatzes, als auch jede Menge Attribute für das Update. Dafür lesen wir die it_keytab analog zur GET_ENTITY Methode aus und die übergebenen Daten analog zu CREATE_ENTITY. Nach welchem Schema ihr das Update nun in eurem Backend durchführt, ist euch überlassen. Falls ihr euch das Auslesen aus der Keytab an dieser Stelle sparen wollt, findet ihr den Schlüssel auch in den ausgelesenen Attributen eurer Entität.
Ihr könntet nun auch analog zu vorigen Methoden Rückmeldung zum Erfolg der Methode geben, aber auch ohne einen Rückgabewert zu befüllen, läuft der erfolgreiche HTTP Request auf eine grüne 204:
![UPDATE_ENTITY]()
Zum Testen im Gateway-Client habe ich mir analog zu CREATE_ENTITY einen Datensatz vorbereitet, die HTTP Methode auf „Put“ gestellt und in der URL wieder eine ID mitgeliefert:
![Entity Updated]()
DELETE_ENTITY
Der letzte ODATA Service befasst sich mit dem Löschen eines Datensatzes. Hierfür müsst ihr lediglich eine ID übergeben. Das vorgehen in der DELETE_ENTITY Methode ist daher wieder ein Auslesen der it_keytab analog zu GET_ENTITY. Bei Bedarf könnt ihr auch hier wieder Informationen über den gelöschten Datensatz ans Frontend übergeben, andernfalls kommt ihr analog zum Update auf den HTTP Code 204:
![DELETE_ENTITY]()
Zum Testen im Gateway-Client setzt ihr die HTTP Methode auf „Delete“ und wählt die URL mit ID analog zu GET_ENTITY:
![Delete Entity Gatway-Client]()
Nun haben wir alle 5 ODATA Services kennen gelernt, für unser Beispiel implementiert und im Gateway-Client getestet. Im nächsten Teil der Reihe werden wir diesen ODATA Service weiterverwenden und die entsprechende Fiori Anwendung dafür entwerfen.
Wenn der OData Service zu Fehlern führt [Whitepaper]
Mit der App-Entwicklung in Fiori hat sich eine Sache grundlegend geändert: Es ist ein Zugriff über die OData Services notwendig.
Hat es dir gefallen?
Wenn es dir gefallen hat, schau doch gerne bei der ODATA Grundlagen Reihe vorbei. Dort gibt es weitere Grundlagen praktisch erklärt!
SAPUI5: ODATA Grundlagen in der praktischen Verwendung
The post ODATA Grundlagen: Services anlegen und testen appeared first on ActivateHR.