restclient — n_pbt_restclient #
← Komponentenreferenz · Inhalt des Handbuchs
Ein HTTPS-Client für PowerBuilder, Version 10 eingeschlossen: GET, POST, PUT, PATCH, DELETE, Header, ein JSON-Körper, ein Datei-Download. Nativ — TLS, Proxy und Dekompression des Systems, und keine CORS-Mauer, weil die Anfrage nicht von einer Seite ausgeht.
▶ Live ansehen — Demoanwendung, Kachel REST client: die Antworten, der Code, der sie holt, und diese Seite nebeneinander (Internetverbindung erforderlich).
Kurzüberblick #
| Nichtvisuelles Objekt | n_pbt_restclient |
| Wofür | Eine REST-API aus einer PowerBuilder-Anwendung aufrufen: lesen, anlegen, aktualisieren, herunterladen |
| Prinzip | Eine Anfrage ist ein Aufruf, der den HTTP-Status liefert; die Antwort wartet im Objekt, gelesen per of_response_text oder of_json_value |
| Abhängigkeit | WinHTTP in der DLL der Bibliothek — keine Seite, keine zusätzliche Runtime |
Schnellstart #
n_pbt_restclient lnv_rest
n_pbt_json lnv_j
long ll_status
lnv_rest = create n_pbt_restclient
lnv_rest.is_base_url = "https://api.example.com"
lnv_rest.of_set_bearer(ls_token)
// Read : the status comes back, the body waits in the object
ll_status = lnv_rest.of_get("/orders/4152")
if ll_status = 200 then ls_customer = lnv_rest.of_json_value("customer")
// Create : the body is built with n_pbt_json, never by hand
lnv_j.of_set_number("order", 4152)
lnv_j.of_set_string("status", "shipped")
ll_status = lnv_rest.of_post("/shipments", lnv_j.of_text())
if ll_status < 0 then MessageBox("API", lnv_rest.is_last_error)
destroy lnv_rest
Jede HTTP-Methode liefert den Status, den der Server gewählt hat — ein 404 ist eine Antwort, kein Fehler — oder einen negativen Code, wenn die Anfrage nicht durchkam: -5 ungültige URL, -4 Fehlschlag (kein Netz, unbekannter Name, Zeitüberschreitung, TLS; is_last_error sagt welcher), -6 im Demomodus verweigert.
Warum nativ, und nicht die Seite #
Ein fetch() aus einer Seite der Bibliothek unterliegt CORS: jede API, die kein Access-Control-Allow-Origin sendet, ist ihm verschlossen — und Unternehmens-APIs senden es nicht. Die Anfrage geht daher von der DLL aus, über WinHTTP: das TLS des Systems mit seinen Zertifikaten, der konfigurierte Proxy, gzip. Was Sie verlieren: nichts; was Sie gewinnen: jede API.
Eigenschaften #
| Eigenschaft | Typ | Standard | Rolle |
|---|---|---|---|
is_base_url | string | "" | Vor eine relative URL gesetzt: is_base_url = "https://api.example.com", dann of_get("/orders/4152"). Eine absolute URL wird unverändert verwendet |
il_timeout_ms | long | 30000 | Maximale Dauer einer Anfrage, Verbindung und Antwort eingeschlossen. Danach: -4 und „timed out“ |
is_last_error | string | "" | Warum die letzte Anfrage einen negativen Code lieferte. Leer nach einer Anfrage, die den Server erreicht hat — ein 404 ist kein Fehler |
il_status | long | 0 | Der HTTP-Status der letzten Anfrage oder ihr negativer Code; der Wert, den die Anfrage lieferte |
Konstanten: METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_PATCH, METHOD_DELETE für of_request.
Methoden #
| Methode | Rolle |
|---|---|
of_set_header (string as_name, string as_value) | Ein Header, der ab jetzt mit jeder Anfrage gesendet wird; ein bereits gesetzter Name wird ersetzt. Content-Type wird automatisch ergänzt, wenn ein Körper ohne ihn gesendet wird (JSON, UTF-8) |
of_remove_header (string as_name) | Vergisst einen Header, nach Name (Groß-/Kleinschreibung egal) |
of_clear_headers ( ) | Vergisst alle Header, Authentifizierung eingeschlossen |
of_set_bearer (string as_token) | Authorization: Bearer bei jeder Anfrage; ein leeres Token entfernt ihn |
of_set_basic (string as_user, string as_password) | Basic-Authentifizierung; die DLL kodiert Base64 selbst — PowerBuilder 10 hat keines |
of_get (string as_url) → long | GET. Liefert den HTTP-Status oder -5 ungültige URL, -4 Fehlschlag, -6 in der Demo verweigert |
of_post (string as_url, string as_body) → long | POST mit einem Körper (standardmäßig JSON, mit n_pbt_json gebaut). Liefert den HTTP-Status oder -5 / -4 / -6 |
of_put (string as_url, string as_body) → long | PUT mit einem Körper. Liefert den HTTP-Status oder -5 / -4 / -6 |
of_patch (string as_url, string as_body) → long | PATCH mit einem Körper. Liefert den HTTP-Status oder -5 / -4 / -6 |
of_delete (string as_url) → long | DELETE. Liefert den HTTP-Status oder -5 / -4 / -6 |
of_request (string as_method, string as_url, string as_body) → long | Jede Methode (METHOD_* oder eine eigene), jeder Körper als UTF-8: was die fünf Kurzformen aufrufen. Liefert den HTTP-Status oder -5 / -4 / -6 |
of_download (string as_url, string as_path) → long | GET direkt in eine Datei, Bytes unverändert, unabhängig von der Größe. Liefert den HTTP-Status oder -5 URL oder Pfad leer, -4 Fehlschlag, -6 in der Demo verweigert |
of_response_text ( ) → string | Der Körper der letzten Antwort als Text (UTF-8 dekodiert) |
of_response_headers ( ) → string | Alle Header der letzten Antwort, einer pro Zeile |
of_response_header (string as_name) → string | Ein Header der letzten Antwort, nach Name; leer, wenn er nicht gesendet wurde |
of_json_value (string as_key) → string | Ein Wert des JSON-Körpers nach seinem Schlüssel, das erste Vorkommen in beliebiger Tiefe. Für mehr als einen Wert of_response_text und n_pbt_utils.of_json_get_* lesen |
of_reset ( ) | Zurück zu den Standardwerten: keine Header, keine Basis-URL, Standard-Timeout, letzte Antwort vergessen |
Beispiele #
Eine Liste lesen und durchgehen #
string ls_body
if lnv_rest.of_get("/orders?status=open") = 200 then
ls_body = lnv_rest.of_response_text()
// One value : of_json_value. The whole list : n_pbt_utils.of_json_get_payload and
// of_json_get_str on each object, or your own parser on ls_body.
end if
Ein Dokument herunterladen #
long ll_status
ll_status = lnv_rest.of_download("/orders/4152/invoice.pdf", "C:\temp\invoice-4152.pdf")
if ll_status = 200 then
Run("C:\temp\invoice-4152.pdf")
elseif ll_status < 0 then
MessageBox("Download", lnv_rest.is_last_error)
end if
Eine API, die mit einem Fehler antwortet #
choose case lnv_rest.of_post("/shipments", ls_body)
case 200, 201
// created
case 401
MessageBox("API", "Token expired : " + lnv_rest.of_json_value("message"))
case is < 0
MessageBox("Network", lnv_rest.is_last_error)
end choose
Asynchron (kein UI-Einfrieren) #
Die synchronen Aufrufe oben blockieren das Skript (die UI bleibt lebendig, aber die Zeile wartet). Die asynchrone API blockiert nicht einmal das Skript: die Anfrage läuft auf einem Arbeitsthread und ihre Antwort kommt später als Ereignis.
Einrichtung — nichts zu verdrahten: die Komponente liefert die Antwort selbst in ue_response. of_open / of_close öffnen und schließen den Client (optional: der erste async-Aufruf öffnet ihn), of_clear_cookies vergisst die zwischen Anfragen gehaltenen Cookies. Intern ruft der Pump der Komponente of_process_events auf, um die Antworten zu leeren — Sie rufen es nie auf.
Senden — of_request_async(method, url, body) oder die Kurzformen of_get_async, of_post_async, of_put_async, of_patch_async, of_delete_async liefern sofort eine Anfrage-id. of_download_async(url, path) lädt in eine Datei, of_upload(url, field, path) lädt eine Datei als multipart/form-data hoch — beide mit Fortschritt. of_cancel(id) stoppt eine laufende Anfrage. Wiederholungen (il_max_retries, il_retry_backoff_ms) bei 429/503/Netzfehler.
Empfangen — ue_response(al_id, al_status) (den Körper mit of_response_text(al_id) / of_status(al_id) IM Handler lesen), ue_failed(al_id, as_error), ue_progress(al_id, al_done, al_total).
| Methode | Rolle |
|---|---|
of_open ( ) → long | Öffnet den asynchronen Client, einen nativen Ereigniskanal (kein WebView, kein CORS). Optional: der erste async-Aufruf öffnet ihn. Liefert die Client-Id (> 0) oder einen negativen Code |
of_close ( ) | Schließt den asynchronen Client: seine Cookies (die Sitzung) gehen mit ihm. Der nächste async-Aufruf öffnet ihn wieder. Von of_reset und bei der Zerstörung für Sie erledigt |
of_process_events ( ) | Leert die asynchronen Antworten und löst ue_response / ue_failed / ue_progress aus. NUR öffentlich, weil der interne Pump der Komponente sie auf der PowerBuilder-Schleife aufruft, alle paar Millisekunden, solange eine Anfrage läuft — Sie rufen sie nie auf und verdrahten weder Empfänger noch Timer. Ein eigener Aufruf ist harmlos: sie leert nur, was bereits wartet |
of_clear_cookies ( ) | Vergisst die zwischen Anfragen gehaltenen Cookies (ein Logout). Die Einstellung ib_keep_cookies bleibt unverändert |
of_get_async (string as_url) → long | Asynchrones GET. Liefert sofort eine Anfrage-id; die Antwort kommt in ue_response(id, status). Liefert -5 bei ungültiger URL, -2 wenn der Client nicht öffnen kann |
of_post_async (string as_url, string as_body) → long | Asynchrones POST mit Körper (standardmäßig JSON, mit n_pbt_json gebaut). Liefert sofort eine Anfrage-id; die Antwort kommt in ue_response. Liefert -5 / -2 wie of_get_async |
of_put_async (string as_url, string as_body) → long | Asynchrones PUT mit Körper. Liefert sofort eine Anfrage-id; die Antwort kommt in ue_response. Liefert -5 / -2 wie of_get_async |
of_patch_async (string as_url, string as_body) → long | Asynchrones PATCH mit Körper. Liefert sofort eine Anfrage-id; die Antwort kommt in ue_response. Liefert -5 / -2 wie of_get_async |
of_delete_async (string as_url) → long | Asynchrones DELETE. Liefert sofort eine Anfrage-id; die Antwort kommt in ue_response. Liefert -5 / -2 wie of_get_async |
of_request_async (string as_method, string as_url, string as_body) → long | Asynchrone Anfrage mit der HTTP-Methode Ihrer Wahl (of_get_async und seine Geschwister rufen sie auf). Liefert sofort eine Anfrage-id; die Antwort kommt in ue_response, ein Fehlschlag in ue_failed. Liefert -2, wenn der Client nicht geöffnet werden kann |
of_download_async (string as_url, string as_path) → long | Asynchrones GET direkt in eine DATEI, mit ue_progress unterwegs. Liefert eine Anfrage-id, -5 bei leerem Pfad, -2, wenn der Client nicht geöffnet werden kann |
of_upload (string as_url, string as_field, string as_path) → long | Lädt eine DATEI als multipart/form-data hoch (Formularfeld as_field), asynchron, mit ue_progress. Liefert eine Anfrage-id, -5 bei leerem Pfad, -2, wenn der Client nicht geöffnet werden kann, -6 im Demomodus |
of_cancel (long al_id) → long | Bittet eine laufende Anfrage anzuhalten; sie endet mit ue_failed(id, "cancelled"). Liefert 0, oder -1, wenn die id unbekannt ist |
of_status (long al_id) → long | Der HTTP-Status einer asynchronen Antwort, nach Anfrage-id - INNERHALB von ue_response lesen. Liefert den Status (200, 404...), 0, wenn die Anfrage unbekannt oder schon vergessen ist |
| Ereignis | Rolle |
|---|---|
ue_response (long al_id, long al_status) | Eine asynchrone Anfrage ist beendet: al_id ist die beim Senden gelieferte id, al_status der HTTP-Status. Den Rumpf mit of_response_text(al_id) INNERHALB des Handlers lesen: die Anfrage ist nach dem Ereignis vergessen |
ue_failed (long al_id, string as_error) | Eine asynchrone Anfrage ist fehlgeschlagen: kein Netz, Zeitüberschreitung, TLS, oder durch of_cancel abgebrochen (as_error ist dann cancelled) |
ue_progress (long al_id, long al_done, long al_total) | Fortschritt eines asynchronen Downloads (of_download_async) oder Uploads (of_upload): al_done Bytes von al_total |
// Async : the UI never freezes, and there is nothing to wire.
ll_id = uo_rest.of_get_async("https://api.example.com/orders/4152")
// The answer arrives on its own in the ue_response event of uo_rest :
IF al_status >= 200 AND al_status < 300 THEN
ls_body = uo_rest.of_response_text(al_id)
END IF
Bewährte Praxis #
- Ein Client pro API, mit einmal gesetzter
is_base_urlund Headern: der Aufrufcode trägt nur noch Pfad und Körper. - Der Körper wird mit
n_pbt_jsongebaut, nie per Verkettung: ein Anführungszeichen in einem Kundennamen zerstört nichts. - Erst den Status prüfen, dann den negativen Code:
>= 200 und < 300ist gut,>= 400der Server hat nein gesagt,< 0die Anfrage ist nicht losgegangen —is_last_errorsagt warum. - Das Timeout: 30 Sekunden standardmäßig; ein schwerer Bericht verdient mehr, ein Health-Check weniger.