Internet (HTTP)¶
Mit http ruft ein Skript Webadressen und Schnittstellen („APIs") auf:
Wechselkurse abfragen, eine Adresse prüfen lassen, Daten an ein anderes
System schicken. Ein Aufruf besteht immer aus einer Anfrage (Adresse,
Methode, eventuell Daten) und einer Antwort (Statuscode und Inhalt).
| Methode | Wofür |
|---|---|
GET |
etwas abrufen |
POST |
etwas Neues senden |
PUT / PATCH |
etwas ändern (ganz / teilweise) |
DELETE |
etwas löschen |
Ausführliches dazu: HTTP-Client.
Die Beispiele sprechen mit KCForms selbst
Damit die Beispiele überall funktionieren, rufen sie Übungs-Adressen
auf, die KCForms selbst mitbringt (http://localhost:8099/api/demo/http/…).
localhost ist aus Sicherheitsgründen gesperrt, bis der Admin es unter
HTTP-/WebService-Konnektoren → Erlaubte Ziel-Hosts freigibt. Echte
Dienste im Internet funktionieren ohne Freigabe — du tauschst nur die
Adresse aus. Die Übungs-Adressen selbst sind in einer produktiven
Installation abgeschaltet; der Admin schaltet sie mit
KCFORMS_DEMO_ENDPOINTS=true ein (siehe
Schutz im Browser).
Die Übungs-Adresse …/echo schickt zurück, was sie bekommen hat. So siehst
du genau, was dein Skript gesendet hat.
Einsteiger¶
1. Eine Adresse abrufen¶
http.get holt eine Adresse. status ist der Statuscode der Antwort —
200 heißt „alles gut".
2. Den Inhalt der Antwort lesen¶
text enthält die Antwort als Text.
3. Eine JSON-Antwort auswerten¶
Die meisten Schnittstellen antworten in JSON. r.json() macht daraus
ein Objekt, dessen Werte du direkt liest.
var r = http.get("http://localhost:8099/api/demo/http/echo");
var daten = r.json();
log.info("Methode: " + daten.method);
log.info("Dienst: " + daten.service);
4. Werte in der Adresse mitschicken¶
query hängt Werte an die Adresse an (?stadt=K%C3%B6ln&plz=50667) und
kümmert sich um Sonderzeichen.
var r = http.get("http://localhost:8099/api/demo/http/echo", {
query: { stadt: "Köln", plz: "50667" }
});
log.info("Angekommen: " + r.json().query.stadt);
5. Prüfen, ob es geklappt hat¶
ok ist true, wenn der Status zwischen 200 und 299 liegt. Ein Fehler wie
404 (nicht gefunden) bricht das Skript nicht ab — du musst selbst
nachsehen.
var r = http.get("http://localhost:8099/api/demo/http/status/404");
if (r.ok) {
log.info("Hat geklappt");
} else {
log.warn("Aufruf nicht erfolgreich: " + r.status);
}
6. Daten als JSON senden¶
http.post(adresse, daten) schickt ein Objekt als JSON.
var r = http.post("http://localhost:8099/api/demo/http/echo", {
name: "Anna",
bestellung: 4711
});
log.info("Gesendet: " + r.json().body.name);
7. Formularwerte senden¶
Genauso mit Werten aus dem Formular.
var r = http.post("http://localhost:8099/api/demo/http/echo", {
name: ctx.form.name,
email: ctx.form.email
});
log.info(r.status + " – angekommen: " + r.json().body.name);
8. Häufige Statuscodes¶
function bedeutung(code) {
if (code >= 200 && code < 300) return "alles gut";
if (code === 401 || code === 403) return "keine Berechtigung";
if (code === 404) return "nicht gefunden";
if (code >= 500) return "Fehler beim Gegenüber";
return "anderer Status";
}
[200, 404, 500].forEach(function (code) {
var r = http.get("http://localhost:8099/api/demo/http/status/" + code);
log.info(r.status + ": " + bedeutung(r.status));
});
Fortgeschritten¶
9. Zusätzliche Header senden¶
Header sind Zusatzangaben zur Anfrage, etwa die gewünschte Sprache oder ein Schlüssel.
var r = http.get("http://localhost:8099/api/demo/http/echo", {
headers: { "Accept-Language": "de-DE", "X-Kunden-Id": "K-100" }
});
log.info("Status " + r.status);
10. Als klassisches Webformular senden¶
Manche ältere Dienste erwarten die Daten wie ein HTML-Formular
(form-urlencoded) statt als JSON.
var r = http.post("http://localhost:8099/api/demo/http/echo", null, {
form: { name: "Anna", ort: "Köln" }
});
log.info("So kam es an: " + r.json().body);
11. Ändern und löschen¶
put, patch und del funktionieren wie post und get.
var basis = "http://localhost:8099/api/demo/http/echo";
log.info(http.put(basis, { status: "erledigt" }).json().method); // PUT
log.info(http.patch(basis, { status: "offen" }).json().method); // PATCH
log.info(http.del(basis).json().method); // DELETE
12. Nicht ewig warten¶
timeout begrenzt die Wartezeit (in Millisekunden). Antwortet der Dienst
nicht rechtzeitig, gibt es einen Fehler, den du abfängst.
var start = Date.now();
try {
var r = http.get("http://localhost:8099/api/demo/http/echo", { timeout: 3000 });
log.info("Antwort in " + (Date.now() - start) + " ms");
} catch (e) {
log.warn("Keine Antwort innerhalb von 3 Sekunden: " + e.message);
}
13. Bei Fehlern gleich abbrechen¶
Mit throwOnError: true wird jeder Status außerhalb von 2xx zu einem
Fehler — praktisch mit try … catch.
try {
http.get("http://localhost:8099/api/demo/http/status/503", { throwOnError: true });
log.info("hat geklappt");
} catch (e) {
log.warn("Fehlgeschlagen: " + e.message);
}
14. Weiterleitungen¶
Leitet eine Adresse weiter, folgt http automatisch. r.url zeigt, wo die
Anfrage am Ende gelandet ist.
var r = http.get("http://localhost:8099/api/demo/http/redirect");
log.info("Gelandet bei: " + r.url);
15. Den Typ der Antwort prüfen¶
contentType sagt, was zurückkam — JSON, HTML, ein Bild … Erst dann
json() aufzurufen verhindert Fehler bei unerwarteten Antworten.
var r = http.get("http://localhost:8099/api/demo/http/echo");
if (r.contentType && r.contentType.indexOf("json") >= 0) {
log.info("JSON erhalten: " + r.json().service);
} else {
log.warn("Unerwartete Antwort: " + r.contentType);
}
16. Eine Datei herunterladen¶
http.download schreibt die Antwort direkt in die Datei-Sandbox
— auch sehr große Dateien, ohne den Speicher zu füllen.
var d = http.download("http://localhost:8099/api/demo/http/download", "/beispiele/http/demo.txt");
log.info(d.bytes + " Byte gespeichert unter " + d.path);
17. Eine Datei hochladen¶
Hochladen geht als multipart — so wie ein Datei-Feld auf einer Webseite.
var r = http.post("http://localhost:8099/api/demo/http/upload", null, {
multipart: [
{ name: "datei", filePath: "/beispiele/http/demo.txt", filename: "demo.txt" },
{ name: "beschreibung", value: "Beispiel-Upload" }
]
});
r.json().parts.forEach(function (teil) {
if (teil.filename) log.info("hochgeladen: " + teil.filename + " (" + teil.size + " Byte)");
});
18. Mit Benutzername und Passwort anmelden¶
Viele Schnittstellen verlangen eine Anmeldung. Bei Basic-Auth gibst du Benutzer und Passwort mit. Echte Zugangsdaten gehören nicht ins Skript, sondern in einen Konnektor oder die Properties.
var r = http.get("http://localhost:8099/api/demo/http/echo", {
auth: "basic", username: "demo", password: "demo"
});
log.info("Angemeldet mit: " + r.json().headers.authorization); // die Übung zeigt es maskiert
Profi¶
19. Eine Sitzung mit Cookies¶
Manche Dienste merken sich die Anmeldung in einem Cookie. Eine
session schickt es bei jedem weiteren Aufruf automatisch mit.
var s = http.session({ baseUrl: "http://localhost:8099/api/demo/http" });
s.get("/cookie"); // der Dienst setzt ein Cookie
var zweiter = s.get("/cookie").json();
log.info("Cookie zurückgeschickt: " + zweiter.cookieYouSent);
log.info("Cookies in der Sitzung: " + s.cookieCount());
Mit baseUrl reichen danach kurze Pfade wie /cookie.
20. Bei Störungen automatisch wiederholen¶
retries wiederholt den Aufruf, wenn der Dienst kurz überlastet ist
(Status 429 oder 5xx) — mit wachsender Pause dazwischen.
var start = Date.now();
var r = http.get("http://localhost:8099/api/demo/http/status/503", {
retries: { count: 2, delayMs: 200 }
});
log.info("Nach 3 Versuchen: Status " + r.status + " (" + (Date.now() - start) + " ms)");
21. Mit einem Zugangs-Token arbeiten (OAuth2)¶
Moderne Schnittstellen verlangen erst ein Token, das dann bei jedem Aufruf mitgeschickt wird.
var token = http.oauth2ClientCredentials({
tokenUrl: "http://localhost:8099/api/demo/http/token",
clientId: "demo",
clientSecret: "demo"
});
var r = http.get("http://localhost:8099/api/demo/http/echo", { auth: "bearer", token: token });
log.info("Mit Token angemeldet: " + r.json().headers.authorization);
22. Seitenweise abrufen¶
Große Listen liefern Schnittstellen in „Seiten". Eine Schleife holt so lange die nächste Seite, bis nichts mehr kommt. Hier simuliert: nach drei Seiten ist Schluss.
var seite = 1, alle = [];
while (seite <= 10) { // Sicherheitsgrenze gegen Endlosschleifen
var r = http.get("http://localhost:8099/api/demo/http/echo", { query: { page: seite } });
var antwort = r.json();
alle.push("Seite " + antwort.query.page);
if (seite === 3) break; // echter Dienst: z. B. if (!antwort.next) break;
seite++;
}
log.info(alle.length + " Seiten geholt");
23. Einen benannten Konnektor verwenden¶
Adresse, Anmeldung und Zeitlimit pflegt der Admin zentral unter HTTP-/WebService-Konnektoren. Im Skript steht dann nur noch der Name.
var api = http.connect("demo-http"); // Adresse steht im Konnektor
var r = api.get("/echo", { query: { quelle: "konnektor" } });
log.info("über Konnektor: " + r.json().query.quelle);
So bleiben Passwörter aus dem Skript heraus, und beim Umzug auf ein anderes System ändert der Admin nur den Konnektor.
24. Ein fremdes System füllt das Formular¶
Die Kundennummer geht an eine Schnittstelle, die Antwort landet in den Formularfeldern. Fehler werden am Feld gemeldet.
try {
var r = http.post("http://localhost:8099/api/demo/http/echo",
{ kundennr: ctx.form.kundennr, name: "Bäckerei Schmitz", ort: "Köln" },
{ timeout: 5000, throwOnError: true });
var kunde = r.json().body; // ein echter Dienst liefert hier die Stammdaten
ctx.form.name = kunde.name;
ctx.form.land = kunde.ort;
log.info("Stammdaten übernommen: " + kunde.name);
} catch (e) {
ctx.addError("kundennr", "Stammdaten nicht abrufbar: " + e.message);
}
25. Ein wiederverwendbarer, robuster Aufruf¶
Eine eigene Funktion bündelt Zeitlimit, Wiederholung und Fehlerbehandlung. Scheitert alles, gibt sie einen Ersatzwert zurück, statt das Skript abzubrechen.
function holeJson(adresse, ersatz) {
try {
var r = http.get(adresse, { timeout: 4000, retries: { count: 1, delayMs: 200 } });
if (!r.ok) {
log.warn(adresse + " antwortet mit " + r.status);
return ersatz;
}
return r.json();
} catch (e) {
log.warn(adresse + " nicht erreichbar: " + e.message);
return ersatz;
}
}
var daten = holeJson("http://localhost:8099/api/demo/http/status/500", { kurs: 1.0 });
log.info("Ersatzwert verwendet: Kurs " + daten.kurs);
fs["/beispiele/http"].remove(true); // Übungsordner aufräumen