Zum Inhalt

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".

var r = http.get("http://localhost:8099/api/demo/http/echo");
log.info("Status: " + r.status);

2. Den Inhalt der Antwort lesen

text enthält die Antwort als Text.

var r = http.get("http://localhost:8099/api/demo/http/echo");
log.info(r.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