Beispiele rest json

Aus bafbal.de
Zur Navigation springen Zur Suche springen

Daten von einem REST-Server holen, anzeigen und bearbeiten

Der REST-Server

Unter https://api.predic8.de/shop/products/ ist ein REST-Server zu finden, der als Beispiel-Server der Allgemeinheit zur Verfügung steht. Der Aufruf der URL gibt die folgenden Daten zurück:

{
   "meta": {
       "count": 32,
       "limit": 10,
       "page": 1,
       "next_url": "/shop/products/?page=2&limit=10"
   },
   "products": [
       {
           "name": "Bananas",
           "product_url": "/shop/products/3"
       },
       {
           "name": "Oranges",
           "product_url": "/shop/products/10"
       },
         ...
       {
           "name": "Apple",
           "product_url": "/shop/products/18"
       },
       {
           "name": "Green Grapes",
           "product_url": "/shop/products/11"
       }
   ]
}

Daten von mehreren Seiten zusammenfassen

Die Daten sind auf mehrere Seiten aufgeteilt. Für unsere Zwecke müssen wir sie zusammenfassen. Dazu eignet sich die Prozedur #http_loop_json.

In solchen Fällen hat es sich bewährt, das erst mal als Konsolen-Anwendung zu realisieren, und das dann in die konkrete Anwendung zu kopieren. Diese Konsolen-Anwendung würde wie folgt aussehen:

#frm  c="c_test_json"   y=console

#code   url=https://api.predic8.de/shop/products/
#code   nurl="https://api.predic8.de$JSON_VALUE(1,meta.next_url)"
#code   ready=$EMPTY($JSON_VALUE(1,meta.next_url))
#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20

#coutl $JSON_TEXT(1,frm)

#cout  c="c_test_json executed"

Die Prozedur #http_loop_json verwendet y=get als Method, wir wollen schließlich Daten abfragen. Als url verwenden wir die Seite https://api.predic8.de/shop/products/.

Sobald die erste Seite da ist, wird die nächste Seite aufgerufen, und deren URL wird mit nurl ("next URL") angegeben. Dazu wird aus dem Anwort-JSON das Feld meta-next_url extrahiert ($JSON_VALUE(1,meta.next_url), #http_loop_json arbeitet auf dem ersten JSON), davor wird der Rest der erforderlichen URL ergänzt. Daneben benötigt #http_loop_json auch noch einen Hinweis, wann die Schleife beendet werden soll, das ist dann der Fall, wenn meta.next_url leer ist. Sicherheitshalber setzt man bei #http_loop_json noch m=20, nach spätestens 20 Seiten wird also auch abgebrochen.

Da der Server ein abgelaufenes Zertifikat hat, setzen wir isc=Y. Anschließend wird mit #coutl $JSON_TEXT(1,frm) das Ergebnis formatiert ausgegeben:

{
 "products": [
   {
     "name": "Bananas",
     "product_url": "/shop/products/3"
   },
   {
     "name": "Oranges",
     "product_url": "/shop/products/10"
   },
   {
     "name": "Pineapples",
     "product_url": "/shop/products/33"
   },
     ...
   {
     "name": "Pistachio",
     "product_url": "/shop/products/76"
   }
 ],
 "meta": {
   "count": 32,
   "limit": 10,
   "page": 4,
   "previous_url": "/shop/products/?page=3&limit=10"
 }
}

Die Prozedur #http_loop_json fügt bei Arrays auf der obersten Ebene (hier products) weitere Array-Elemente hinzu (die Anzeige eben wurde an der Stelle ... gekürzt, die Liste wäre sonst deutlich länger), während Objekte überschrieben werden (hier meta).

ID extrahieren

Wir können später die ID gebrauchen, von daher wollen wir diese auch extrahieren.

#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20

#cmd_clear #json_chgdata   n=json   path=id   add=Y   is=N   z="$STREXTR($JSON_DATA(json,product_url),ffe,/shop/products/)"
#json_loop   n=json   er=1   path=products

#coutl $JSON_TEXT(1,frm)

Vor der Ausgabe fügen wir eine #json_loop ein, die durch das Array products geht und für jeden Eintrag die zuvor definierte lokale Prozedur aufruft. Hier wird mit #json_chgdata das jeweilige Element in der Loop geändert, genau genommen - da add=Y - wird ein neues Element namens path=id dem jeweiligen Array-Element hinzu. Der Wert von id wird mit dem Parameter z bestimmt. Hier holen wir die product_url aus dem JSON und extrahieren alles, was nach /shop/products/ kommt.

Die Ergebnismenge (Ausschnitt) sieht nun wie folgt aus:

{
 "products": [
   {
     "name": "Bananas",
     "product_url": "/shop/products/3",
     "id": 3
   },
   {
     "name": "Oranges",
     "product_url": "/shop/products/10",
     "id": 10
   },
     ...

Einen Tree füllen

Nun wollen wir mit den Daten in eine konventionelle Oberfläche und legen mit dem Wizard das Kommando xtestrest an. Für das Sub-Kommando xtestrest_flt verwenden wir

#tree_clear

#tree_add   u=root   c=Produkte   si=Y   s="#page_fill   d=xtestrest_page_produkte"  o=xtestrest_open(produkte)

Die Produkte laden wir dann in xtestrest_open.

~ $ICP(0,produkte)
#code   url=https://api.predic8.de/shop/products/
#code   nurl="https://api.predic8.de$JSON_VALUE(1,meta.next_url)"
#code   ready=$EMPTY($JSON_VALUE(1,meta.next_url))
#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20

#cmd_clear #json_chgdata   n=json   path=id   add=Y   is=N   z="$STREXTR($JSON_DATA(json,product_url),ffe,/shop/products/)"
#json_loop   n=json   er=1   path=products

#tree_fill   u=o   q=json   path=products   c1=id   c2=name   s="#page_fill   d=xtestrest_page_produkt   rs=Y"  

~~

Bis auf #tree_fill ist das vom eben erstellten Konsolen-Kommando 1:1 kopiert. Als Datenquelle geben wir q=json ein, also werden die Daten aus dem ersten JSON geholt. Mit ID und name werden die entsprechenden Werte aus dem JSON geholt.

Rest baum.png

Ein VL-Segment füllen

Wenn wir https://api.predic8.de/shop/products/3 aufrufen, dann erhalten wir die Details zum Produkt zurück.

{
   "name": "Bananas",
   "price": 0.99,
   "photo_url": "/shop/products/3/photo",
   "category_url": "/shop/categories/Fruits",
   "vendor_url": "/shop/vendors/672"
}

Diese wollen wir nun in einem VL-Segment anzeigen. Das sieht erst mal wie folgt aus:

#grid

#prim  as=Y  
#cat  as=Y     c="Daten aus https://api.predic8.de/shop/products/"

#vl_seg   cc=5   w1=70   w2=50  w3=20   w4=70   w5=200   n=vl   
#vl_line   c1=ID   c2=$FND(s,id)   nd2=Y   nvi2=neu   c3=" "   c4=Name   f5=name
#vl_line   c1=Preis   f2=price   y2=curr   a2=d2   c3=" "   c4=Kategorie   f5=category_url   
#vl_line   c1=Lieferant   c3=" "   c4=Name   f5=vendor_url   

#http_request   y=get   se=N   response=resp   isc=Y    url=https://api.predic8.de/shop/products/$FND(s,id)  
#json_parse   json=$VAR(resp)

#vl_data   q=json   k=id    isc=Y  

Rest vl1.png

Das zeigt ja immerhin schon mal die Daten an, aber ansonsten gibt es noch einiges zu tun.

Zunächst einmal haben wir bei der Kategorie und dem Lieferanten Verweise auf andere Seiten. Bei der Kategorie könnte man mit der Funktion $STREXTR() die Kategorie rausziehen, aber das würde dann zum Problem, wenn wir die Daten ändern wollen. Also machen wir es gleich richtig und legen eine Nachschlageliste an:

Nachschlagelisten

Für die Nachschlagelisten nutzen wir jetzt noch mal c_test_json und bauen eine Text-ValueList erst mal in der Konsolen-Anwendung auf. Zunächst sieht das mal ziemlich ähnlich wie eben aus, wir nutzen wieder #http_loop_json (im Moment haben wir nur eine Seite, aber es könnten ja mehr werden...), lediglich products wird durch vendors ersetzt.

#frm  c="c_test_json"   y=console

#code   url=https://api.predic8.de/shop/vendors/
#code   nurl="https://api.predic8.de$JSON_VALUE(1,meta.next_url)"
#code   ready=$EMPTY($JSON_VALUE(1,meta.next_url))
#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20
#json_tvl   n=1   path=vendors   ckey=vendor_url   cvalue=name

#coutl $TEXT(1)

#cout  c="c_test_json executed"

Mit #json_tvl wird das JSON in eine Text-ValueList übersetzt, da n=1, ist das Ergebnis dann im ersten Text. Der Pfad zum Array ist vendors, in Anlehnung der Spaltenbenennung in SQL-Nachschlagelisten heißen die Parameter für die einzelnen Felder dann ckey und cvlaue. Wir bekommen dann das folgende Ergebnis:

/shop/vendors/672=Western Tasty Fruits Ltd.
/shop/vendors/32=Exotic Fruits Company
/shop/vendors/501=Home Fruits
/shop/vendors/810=Fun Fresh Fruits Ltd.
/shop/vendors/67=Nuts for Nuts Company

Diese Nachschlageliste können wir dann in unserem VL-Segment verwenden, und für die Kategorien machen wir das entsprechend:

-- Nachschlageliste Verndors
#code   url=https://api.predic8.de/shop/vendors/
#code   nurl="https://api.predic8.de$JSON_VALUE(1,meta.next_url)"
#code   ready=$EMPTY($JSON_VALUE(1,meta.next_url))
#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20
#json_tvl   n=1   path=vendors   ckey=vendor_url   cvalue=name

-- Nachschlageliste Categories
#code   url=https://api.predic8.de/shop/categories/
#code   nurl="https://api.predic8.de$JSON_VALUE(1,meta.next_url)"
#code   ready=$EMPTY($JSON_VALUE(1,meta.next_url))
#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20
#json_tvl   n=2   path=categories   ckey=category_url   cvalue=name

#vl_seg   cc=5   w1=70   w2=50  w3=20   w4=70   w5=200   n=vl   
#vl_line   c1=ID   c2=$FND(s,id)   nd2=Y   nvi2=neu   c3=" "   c4=Name   f5=name
#vl_line   c1=Preis   f2=price   y2=curr   a2=d2   c3=" "   c4=Kategorie   f5=category_url   y5=lookup   ld5=tvl2
#vl_line   c1=Lieferant   c3=" "   c4=Name   f5=vendor_url   y5=lookup   ld5=tvl1

Die entsprechenden Felder werden mit y5=lookup auf Nachschlageliste umgestellt, und mit ld5=tvl1 beziehungsweise ld5=tvl2 werden die Werte aus dem ersten oder zweiten Text gezogen. Wichtig dann auch, dass bei der zweiten Prozedur #json_tvl n=2 gesetzt wird.

Rest vl2.png

Daten ergänzen

Wir wollen jetzt noch die Lieferantennummer anzeigen. Daten lassen sich einfach ergänzen, indem man das JSON ergänzt. Wir fügen also nach #json_parse die Prozedur #json_chg ein, setzen aa=y und extrahieren - wie oben schon beim Produkt - die ID:

#json_parse   json=$VAR(resp)
#json_chg   path=vendor_id   add=Y   z="$STREXTR($JSON_VALUE(1,vendor_url),ffe,/shop/vendors/)"

Damit können wir nun das VL-Segment ergänzen. Wir werden später Daten zurückschreiben, da können wir mit dieser vendor_id nichts anfangen, somit setzen wir nd2=Y ("no data"):

#vl_line   c1=Lieferant    f2=vendor_id   ro2=Y   nd2=Y  c3=" "   c4=Name   f5=vendor_url   y5=lookup   ld5=tvl1

Bilder anzeigen

Wenn wir schon in den Daten eine photo_url haben, dann wollen wir auch das entsprechende Bild laden und anzeigen. Dazu ergänzen wir eine weitere Primärregion:

#prim  as=Y  
#cat  as=Y     c="Bild"

~ $EMPTY($JSON_VALUE(1,photo_url))
#text_seg
#text_line Kein Bild vorhanden

~
#pic_seg   ho=300   q=http   url=https://api.predic8.de/shop/products/$FND(s,id)/photo   isc=Y    sv=N   sh=N

~~

Wir prüfen erst mal, ob wir überhaupt eine photo_url haben, wenn nicht, setzen wir an die Stelle des Bildes den Hinweis, dass kein Bild vorhanden ist. Ansonsten haben wir ein Picture-Segment mit q=http, dem wir die url des Bildes übergeben müssen. Da der Server immer noch kein gültiges Zertifikat hat, setzen wir isc=Y, daneben blenden wir die Scrollbalken aus.

Rest vl3.png

Produkte verändern

Um Produkte zu ändern, muss bei #vl_data einfach nur die uurl ("Update URL") gesetzt werden. Die Method ist per Default PUT, diese erwartet den ganzen Datensatz, was jetzt grundsätzlich kein Problem wäre, da wir ja alles liefern könnten. Jedoch müsste man dann auch irgendwie das Bild übergeben, sonst ist es danach weg, und ich habe nicht beschrieben gefunden, wie man das mitgibt (wenn es überhaupt geht). Von daher setzen wir mu=patch, damit ist dieses Problem umgangen.

#code uurl=https://api.predic8.de/shop/products/$FND(s,id)   mu=patch
#code iurl=https://api.predic8.de/shop/products/
#vl_data   q=json   k=id    isc=Y    $CODE$

Produkte ergänzen

Um Produkte zu ergänzen, verwenden wir zunächst mal in xtestrest_add die Prozedur #tree_add. Besonderheiten gibt es hier nicht.

~ $ICP(0,root)
#tree_add   u=root   c="Neuer Datensatz"   si=Y   s="#page_fill   d=xtestrest_page_produkt" 

~~

Damit die Datenmenge in den Insert-Status versetzt wird, verwenden wir nvi. Im Gegensatz zu Datenbanken, bei denen wir dort in der Regel $GUID() verwenden, ist es hier völlig egal, was wir reinschreiben, da es ohnehin wegen nd2=Y nicht zu Server übermittelt wird. Von daher ganz pragmatisch nvi2=neu

#vl_line   c1=ID   c2=$FND(s,id)   nd2=Y   nvi2=neu   c3=" "   c4=Name   f5=name

Wenn wir jetzt keine id im Baum haben, wird das mit dem Laden der Daten nicht funktionieren. Dennoch sollten wir #json_parse ausführen, damit das JSON leer ist und nicht der zuletzt verwendete Datensatz drin steht.

~ $NEMPTY($FND(s,id))
-- Daten nur lesen, wenn eine ID vorhanden ist
#http_request   y=get   se=N   response=resp   isc=Y    url=https://api.predic8.de/shop/products/$FND(s,id)  
#json_parse   json=$VAR(resp)
#json_chg   path=vendor_id   add=Y   z="$STREXTR($JSON_VALUE(1,vendor_url),ffe,/shop/vendors/)"

~
#json_parse

~~ 

Das mit dem #http_request, #json_parse und #json_chg hatten wir vorher schon - wir führen es nun halt nur dann aus, wenn wir eine ID haben.

Zuletzt ergänzen wir die iurl ("Insert URL"). Die Standard-Methode für mi ("Method Insert") ist POST, das passt so. Bilder gibt es bei den neu hinzugefügten Produkten damit nicht.

#code uurl=https://api.predic8.de/shop/products/$FND(s,id)   mu=patch
#code iurl=https://api.predic8.de/shop/products/
#vl_data   q=json   k=id    isc=Y    $CODE$

Ein Grid-Segment füllen

Die Seite xtestrest_page_produkte definieren wir erst mal wie folgt:

#page

#prim  as=Y  
#cat  as=Y     c="Daten aus https://api.predic8.de/shop/products/"

#grd_seg   fcc=1   clt=ss   n=grid   c=" "   b=XY
#grd_col   f=id   c1=ID   y=int   w=40   ro=Y   nd=Y   q=json
#grd_col   f=name   c1=Name   w=200   q=json

#code   url=https://api.predic8.de/shop/products/
#code   nurl="https://api.predic8.de$JSON_VALUE(1,meta.next_url)"
#code   ready=$EMPTY($JSON_VALUE(1,meta.next_url))
#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20

#cmd_clear #json_chgdata   n=json   path=id   add=Y   is=N   z="$STREXTR($JSON_DATA(json,product_url),ffe,/shop/products/)"
#json_loop   n=json   er=1   path=products

#grd_data   q=json   path=products  isc=Y   uurl=https://api.predic8.de/shop/products/$PVAL(grid,id,saverow)   mu=patch

Das ist nun ziemlich "gerade aus": Mit #http_loop_json holen wir die Daten über mehrere Seiten, mit #json_loop wird die ID extrahiert und dem JSON hinzugefügt, #grd_data verwendet als Quelle q=json und als Pfad path=products. Wegen des veralteten Server-Zertifikats setzen wir isc=Y, und wir berücksichtigen schon den Update-Fall mit uurl=https://api.predic8.de/shop/products/$PVAL(grid,id,saverow) und mu=patch, aus denselben Gründen wie oben.

Für die Update-URL brauchen wir die ID der Zeile, die gerade gespeichert wird, die ermitteln wir mit $PVAL(grid,id,saverow), saverow ist der Index der Zeile, die gerade gespeichert wird.

Weitere Daten per Thread ergänzen

Jetzt wollen wir nicht nur ID und Name im Grid, sondern auch Preis und Lieferant. Dazu ergänzen wir erst mal diese beiden Spalten im Grid und setzen die Quelle auf q=thread:

#grd_col   f=price   c1=Preis   w=70   y=curr   a=d2   q=thread
#grd_col   f=vendor_url   c1=Lieferant   w=200   q=thread   y=lookup   ld=tvl1

Damit der Anwender weiß, warum da manche Spalten erst mal leer sind, schreiben wir einen entsprechenden Hinweis in den Titel des Grid-Segments:

#grd_seg   fcc=1   clt=ss   n=grid   c="Daten werden aus Thread geladen"   b=XY

Und dann braucht es lediglich noch die Prozedur #grd_thread, welche die Daten holt. Unsere Schlüsselspalte ist k=id, die HTTP-Methode heißt hier method, da y bereits als Parametername "belegt" ist, in der url wird mit :id der Wert der Schlüsselspalte eingefügt:

#grd_thread   i=grid   y=gridjson   k=id   isc=Y   ready=xtestrest_add(ready)   url=https://api.predic8.de/shop/products/:id   method=get

Und dann gibt es noch den Parameter ready. Die entsprechende Routine wird ausgeführt, wenn der Thread abgeschlossen ist. Damit wird der Hinweis "Daten werden aus Thread geladen" entfernt:

~ $ICP(0,ready)
#page_val   i=grid   c=" "   sr=Y

~~

Lieferanten ändern, hinzufügen und löschen

Wir wollen jetzt auch noch Lieferanten bearbeiten können. Dazu wird zunächst im Baum eine weitere Seite ergänzt, wir ändern also xtestrest_flt wie folgt:

#tree_clear 

#tree_add   u=root   c=Lieferanten   s="#page_fill   d=xtestrest_page_lieferanten"  
#tree_add   u=root   c=Produkte   si=Y   s="#page_fill   d_=xtestrest_page_produkte"  o=xtestrest_open(produkte)

Die Seite xtestrest_page_lieferanten sieht wie folgt aus:

#page

#prim  as=Y  
#cat  as=Y     c="Lieferanten"

#btns_seg
#btns_btn   c="Datensatz hinzufügen"   w=180   cmd="#grd_add   i=grid"   se=bcp
#btns_btn   c="Selektierten Datensatz löschen"   w=240   cmd=xtestrest_add(delete_lief)   se=bcp

#code   url=https://api.predic8.de/shop/vendors/
#code   nurl="https://api.predic8.de/shop/products/$STREXTR($JSON_VALUE(1,meta.next_url),ffe,/shop/vendors/)"
#code   ready=$EMPTY($JSON_VALUE(1,meta.next_url))
#http_loop_json   y=get    se=N   $CODE$   isc=Y   m=20 

#cmd_clear #json_chgdata   n=json   path=id   add=Y   is=N   z="$STREXTR($JSON_DATA(json,vendor_url),ffe,/shop/vendors/)"
#json_loop   n=json   er=1   path=vendors

#grd_seg   fcc=1   clt=ss   n=grid   c=" "   b=XY
#grd_col   f=id   c1=ID   y=int   w=40   ro=Y   nd=Y   q=json   nvi=neu
#grd_col   f=name   c1=Name   w=200   q=json

#code uurl=https://api.predic8.de/shop/vendors/$PVAL(grid,id,saverow)   mu=patch
#code iurl=https://api.predic8.de/shop/vendors/
#grd_data   q=json   path=vendors  isc=Y   $CODE$

Aus das ist wieder ziemlich "gerade aus": #grd_data hat jetzt nicht nur uurl, sondern auch iurl, und bei der ID-Spalte ist nvi=neu gesetzt. Neu hinzu gekommen ist die Möglichkeit, den selektierten Datensatz zu löschen. Dazu werden in xtestrest_add die folgenden Zeilen ausgeführt:

~ $ICP(0,delete_lief)
#http_request   y=delete   se=N  isc=Y   url=https://api.predic8.de/shop/vendors/$PVAL(grid,id,sel)

~~

Im Prinzip ist es damit getan, die Methode auf DELETE zu setzen und an den Pfad die ID der selektierten Spalten anzuhängen, wofür wir $PVAL(grid,id,sel) nutzen.