arrow_back history picture_as_pdf This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ~~Title: REST Dienste~~ Wir definieren die Business Logik mit [[de:jvx:server:lco:objects|Life-Cycle Objekten]] am Server. Die Zugriffsberechtigung einer Applikation wird durch einen [[de:jvx:server:security:manager|Security Manager]]geprüft. Der Zugriff auf die Business Logik erfolgt üblicherweise via Master- oder SubConnections vom Client. Um die Technologie Unabhängigkeit zu vollenden, steht die Komplette Business Logik einer Applikation auch via REST zur Verfügung. Für die Benützung der REST Services ist die Autentifizierung mit Benutzername und Passwort notwendig. Als Authentifizierungsmechanismus wird [[https://de.wikipedia.org/wiki/HTTP-Authentifizierung#Basic_Authentication|BASIC]] erwendet Die Überprüfung der Daten wird vom Security Manager der Applikation wie gewohnt durchgeführt. Sie müssen für die Integration der REST Services keine Zeile Source Code verändern. =====Funktionsweise===== Die REST Implementierung in JVx wurde mit [[http://www.restlet.org/|Restlet]] umgesetzt. Um die REST Services zu nutzen, muss der Deployment Deskriptor wie folgt konfiguriert werden: <file xml> <servlet> <servlet-name>RestletServlet</servlet-name> <servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class> <init-param> <param-name>org.restlet.application</param-name> <param-value>com.sibvisions.rad.server.http.rest.RESTAdapter</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>RestletServlet</servlet-name> <url-pattern>/services/rest/*</url-pattern> </servlet-mapping> </file> Mit dieser Konfiguration stehen folgende Services zur Verfügung: ===== Verfügbare Dienste ===== ==== Administration ==== Für die Administration stehen unterschiedliche Dienste zur Verfügung. Diese können im Standardfall nur mittels POST Requests verwendet werden. Falls jedoch benutzerdefinierte Admin-Dienste registriert wurden, können diese auch mittels GET Request angesprochen werden. Folgende Dienste stehen standardmässig zur Verfügung: * [[#anmeldung_testen|Anmeldung testen]] * [[#passwort_aendern|Passwort ändern]] === Anmeldung testen === Anmeldung testen:\\ ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/_admin/**<fc #BF0000>testAuthentication</fc>**'' \\ oder \\ ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/_admin/**<fc #BF0000>testAuthentication</fc>**/**<fc #BF0000>parameter</fc>**'' Der Request benötigt eine HashMap im JSON Format. Beispiel: <file json> { "username" : "admin", "password" : "adminpassword" } </file> Der Benutzername kann auch weggelassen werden. In diesem Fall wird dann der Parameter aus dem URL als Benutzername verwendet. == POST-Response == Wenn die Anmeldung erfolgreich war, wird kein Response generiert und der Status Code ist 204 (SUCCESS_NO_CONTENT). === Passwort ändern === Anmeldung testen:\\ ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/_admin/**<fc #BF0000>changePassword</fc>**'' \\ oder \\ ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/_admin/**<fc #BF0000>changePassword</fc>**/**<fc #BF0000>parameter</fc>**'' Der Request benötigt eine HashMap im JSON Format. Beispiel: <file json> { "username" : "admin", "oldpassword" : "oldpassword", "newpassword" : "newpassword" } </file> Der Benutzername kann auch weggelassen werden. In diesem Fall wird dann der Parameter aus dem URL als Benutzername verwendet. == POST-Response == Wenn das Passwort geändert wurde, wird kein Response generiert und der Status Code ist 204 (SUCCESS_NO_CONTENT). === Benutzerdefinierte Dienste === Wenn man einen eigenen Dienst zur Laufzeit registrieren möchte, kann dies mittels <code java> AdminService.register(String pApplicationName, String pAction, IAdminServiceDelegate pDelegate); AdminService.unregister(String pApplicationName, String pAction, Class<? extends IAdminServiceDelegate> pClass) </code> erfolgen. Der Dienst kann entweder per GET oder POST Request angesprochen werden, je nachdem ob **//IAdminServiceGetDelegate//** oder **//IAdminServicePostDelegate//** verwendet wird. Der Aufruf erfolgt mittels: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/_admin/**<fc #BF0000>ACTION</fc>**'' \\ oder \\ ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/_admin/**<fc #BF0000>ACTION</fc>**/**<fc #BF0000>parameter</fc>**'' ==== Storage Zugriff (CRUD, Meta Data) ==== * [[#get-request_select|Select]] * [[#post-request_insert|Insert]] * [[#put-request_update|Update]] * [[#delete-request_delete|Delete]] * [[#options-request_meta_daten|Meta Daten]] ==GET-Request (Select)== Alle Daten abfragen: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**'' Genau einen Datensatz abfragen: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**/**<fc #BF0000>PRIMARY_KEY</fc>**'' Wenn der PK aus mehreren Spalten zusammengesetzt ist, müssen die Query Parameter verwendet werden: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**?**<fc #BF0000>PKCOLUMN=VALUE</fc>**&**<fc #BF0000>PKCOLUMN2=VALUE2</fc>**'' Die Query Parameter können auch verwendet werden, um Filterungen mit Spalten, die nicht PK Spalten sind, durchzuführen. Lesen Sie mehr über [[https://blog.sibvisions.com/2017/10/30/jvx-rest-interface-update/|komplexe Abfrage Parameter]]? ==GET-Response== Der Response enthält immer eine Liste von HashMaps im JSON Format. Als Key wird die Spaltenbezeichnung verwendet. Beispiel: <file json> [ { "ID" : 0, "POST_ID" : 127, "POST_PLZ" : "1127", "STIEGE" : 8, "STRA_ID" : 68, "STRA_NAME" : "Strasse (69)", "HAUSNUMMER" : 37, "TUERNUMMER" : 79 }, { "ID" : 1, "POST_ID" : 50, "POST_PLZ" : "1050", "STIEGE" : 7, "STRA_ID" : 55, "STRA_NAME" : "Strasse (56)", "HAUSNUMMER" : 37, "TUERNUMMER" : 60 }, ] </file> ==POST-Request (Insert)== Einen neuen Datensatz einfügen: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**'' Der Request benötigt eine HashMap im JSON Format. Als Key wird die Spaltenbezeichnung verwendet. Beispiel: <file json> { "POST_ID" : "0", "STRA_ID" : "0", "HAUSNUMMER" : "9999" } </file> == POST-Response == Der Response liefert den vollständigen Datensatz im JSON Format: <file json> { "ID" : 1008, "POST_ID" : 0, "POST_PLZ" : "1000", "STIEGE" : null, "STRA_ID" : 0, "STRA_NAME" : "Strasse (1)", "HAUSNUMMER" : 9999, "TUERNUMMER" : null } </file> == PUT-Request (Update) == Einen Datensatz per PK aktualisieren: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**/**<fc #BF0000>PRIMARY_KEY</fc>**'' Wenn der PK aus mehreren Spalten zusammengesetzt ist oder der Datensatze nicht über den PK identifiziert werden sollen, müssen die Query Parameter verwendet werden: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**?**<fc #BF0000>COLUMN=VALUE</fc>**&**<fc #BF0000>COLUMN2=VALUE2</fc>**'' Der Request benötigt eine HashMap im JSON Format. Als Key wird die Spaltenbezeichnung verwendet. Beispiel: <file json> { "ID" : "123", "HAUSNUMMER" : "0", "STIEGE" : "0", "TUERNUMMER" : "0" } </file> Es gilt zu beachten, das PK Spalten nicht aktualisiert werden. ==PUT-Response== Der Response liefert den vollständigen Datensatz im JSON Format: <file json> { "ID" : 0, "POST_ID" : 127, "POST_PLZ" : "1127", "STIEGE" : "0", "STRA_ID" : 68, "STRA_NAME" : "Strasse (69)", "HAUSNUMMER" : "0", "TUERNUMMER" : "0" } </file> ==DELETE-Request (Delete)== Genau einen Datensatz löschen: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**/**<fc #BF0000>PRIMARY_KEY</fc>**'' Wenn der PK aus mehreren Spalten zusammengesetzt ist, müssen die Query Parameter verwendet werden: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**?**<fc #BF0000>PKCOLUMN=VALUE</fc>**&**<fc #BF0000>PKCOLUMN2=VALUE2</fc>**'' ==DELETE-Response== Der Response liefert die Anzahl der gelöschten Datensätze im JSON Format (als Nummer): <file json> 42 </file> ==OPTIONS-Request (Meta Daten)== Meta Daten abfragen: ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/data/**<fc #BF0000>STORAGE_NAME</fc>**'' ==OPTIONS-Response== Der Response liefert die Meta Daten im JSON Format: <file json> { "autoIncrementColumnNames" : [ "ID" ], "columnMetaData" : [ { "allowedValues" : null, "autoIncrement" : true, "dataType" : 3, "defaultValue" : null, "label" : "Id", "linkReference" : null, "name" : "ID", "nullable" : false, "precision" : 10, "scale" : 0, "signed" : true, "sqltype" : 4, "writable" : true }, { "allowedValues" : null, "autoIncrement" : false, "dataType" : 3, "defaultValue" : null, "label" : "Post Id", "linkReference" : null, "name" : "POST_ID", "nullable" : false, "precision" : 10, "scale" : 0, "signed" : true, "sqltype" : 4, "writable" : true }, { "allowedValues" : null, "autoIncrement" : false, "dataType" : 12, "defaultValue" : null, "label" : "Plz", "linkReference" : { "columnNames" : [ "POST_ID", "POST_PLZ"], "referencedColumnNames" : [ "ID", "PLZ"], "referencedStorage" : ".subStorages.postleitzahlen" }, "name" : "POST_PLZ", "nullable" : false, "precision" : 2147483647, "scale" : 0, "signed" : false, "sqltype" : 12, "writable" : false }, { "allowedValues" : null, "autoIncrement" : false, "dataType" : 3, "defaultValue" : null, "label" : "Stra Id", "linkReference" : null, "name" : "STRA_ID", "nullable" : false, "precision" : 10, "scale" : 0, "signed" : true, "sqltype" : 4, "writable" : true }, { "allowedValues" : null, "autoIncrement" : false, "dataType" : 12, "defaultValue" : null, "label" : "Name", "linkReference" : { "columnNames" : [ "STRA_ID", "STRA_NAME"], "referencedColumnNames" : [ "ID", "NAME"], "referencedStorage" : ".subStorages.strassen" }, "name" : "STRA_NAME", "nullable" : false, "precision" : 2147483647, "scale" : 0, "signed" : false, "sqltype" : 12, "writable" : false }, { "allowedValues" : null, "autoIncrement" : false, "dataType" : 3, "defaultValue" : null, "label" : "Hausnummer", "linkReference" : null, "name" : "HAUSNUMMER", "nullable" : false, "precision" : 10, "scale" : 0, "signed" : true, "sqltype" : 4, "writable" : true }, { "allowedValues" : null, "autoIncrement" : false, "dataType" : 3, "defaultValue" : null, "label" : "Stiege", "linkReference" : null, "name" : "STIEGE", "nullable" : true, "precision" : 10, "scale" : 0, "signed" : true, "sqltype" : 4, "writable" : true }, { "allowedValues" : null, "autoIncrement" : false, "dataType" : 3, "defaultValue" : null, "label" : "Tuernummer", "linkReference" : null, "name" : "TUERNUMMER", "nullable" : true, "precision" : 10, "scale" : 0, "signed" : true, "sqltype" : 4, "writable" : true } ], "columnNames" : [ "ID", "POST_ID", "POST_PLZ", "STRA_ID", "STRA_NAME", "HAUSNUMMER", "STIEGE", "TUERNUMMER" ], "primaryKeyColumnNames" : [ "ID" ], "representationColumnNames" : [ "ID", "POST_ID", "POST_PLZ", "STRA_ID", "STRA_NAME", "HAUSNUMMER", "STIEGE", "TUERNUMMER" ] } </file> \\ ==== Aufruf von Actions ==== Die [[jvx:communication:calling_server_action|Server-side Actions]] können sowohl direkt vom Life-cycle Objekt als auch von verwendeten Business Objekten aufgerufen werden. Die Parameter-Übergabe ist ebenfalls möglich. * [[#get-request|Action ohne Parameter]] * [[#postput-Request|Action mit Parameter]] == GET-Request == Aufruf einer Server-side Action (ohne Parameter): ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/action/**<fc #BF0000>ACTION_NAME</fc>**'' Aufruf einer Methode eines Business Objektes (ohne Parameter): ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/object/**<fc #BF0000>OBJECT_NAME</fc>**/**<fc #BF0000>ACTION_NAME</fc>**'' == GET-Response == Der Response liefert den Rückgabewert der Action im JSON Format. == POST/PUT-Request == Aufruf einer Server-side Action (mit Parameter): ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/action/**<fc #BF0000>ACTION_NAME</fc>**'' Aufruf einer Methode eines Business Objektes (mit Parameter): ''<nowiki>http://server:port/webapp/services/rest/</nowiki>**<fc #BF0000>APPLICATION_NAME</fc>**/**<fc #BF0000>LIFECYCLE_CLASS</fc>**/object/**<fc #BF0000>OBJECT_NAME</fc>**/**<fc #BF0000>ACTION_NAME</fc>**'' Der Request benötigt ein Array von Objekten, befüllt mit den Parametern für die Action. Beispiel: Action: <file java> public String calculate(Number pFirst, Number pSecond) { return "" + pFirst.intValue() + pSecond.intValue(); } </file> JSON Request: <file json> [123,1] </file> == POST/PUT-Response == Der Response liefert den Rückgabewert der Action im JSON Format. ===== Anwendungsbeispiele ===== ==== Integration ==== Mittels php: <file php> $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,'https://<server>/DB/services/rest/League/Standings/data/All'); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($ch, CURLOPT_USERPWD, "user:password"); //curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); $json = json_decode(curl_exec($ch), true); curl_close($ch); </file> \\ [[https://blog.sibvisions.com/2017/10/23/angularjs-4-with-visionx-and-jvx-rest-services/|AngularJS 4 with VisionX and JVx REST services]] \\ [[https://blog.sibvisions.com/2015/06/15/angularjs-with-jvx-in-action/|AngularJS with JVx in action]] \\ [[https://blog.sibvisions.com/2016/02/29/using-oracle-jet-with-visionxjvx/|Oracle JET with VisionX/JVx]] \\ ==== JUnit Tests ==== [[https://sourceforge.net/p/jvx/code/HEAD/tree/trunk/java/library/test/com/sibvisions/rad/server/http/rest/TestCallService.java|TestCallService]] für die Lifecycle Objekte: [[https://sourceforge.net/p/jvx/code/HEAD/tree/trunk/java/library/rad/apps/demo/src.server/demo/Session.java|Session]] und [[https://sourceforge.net/p/jvx/code/HEAD/tree/trunk/java/library/rad/apps/demo/src.server/demo/special/Address.java|Address]] \\ **<fs 20px>Hinweis</fs>** Bei Action calls müssen die korrekten Datentypen verwendet werden! Generell sollte auf primitive Datentypen, als Parameter, verzichtet und anstatt von Arrays sollte das List Interface verwendet werden. Weiters empfiehlt sich die Nutzung von Number für alle numerischen Werte. Dadurch werden Probleme aufgrund der JSON Serialisierung vermieden. Als Life-Cycle Name sollte der Voll qualifiziert Klassenname, mit Package, verwendet werden. Wenn nur der simple Klassen Name verwendet wird, versucht JVx eine passende Klasse zu ermitteln. Sollten mehrere Klassen in Frage kommen, dann wird keine Klasse verwendet. Sie können optional einen Suchpfad in der config.xml der Applikation definieren: <file xml> <application> .. <rest> <search> <path>/com/sibvisions/app/myapp</app> <path>/com/sibvisions/app/myapp/screens/sub/</app> </search> </rest> </application> </file> Weitere Details über dieses Feature finden Sie im [[https://oss.sibvisions.com/index.php?do=details&task_id=534|Support System]].