mitmario.dev

Die Anfrage lesen

Node.js Sandbox 3 Min Lesezeit 3 BeispieleLektion 3 von 8

req ist alles, was hereinkommt. Drei Dinge davon brauchst du ständig, und mit den ersten beiden fängt jedes Routing an.

Methode und Pfad

Methode und Pfad
import { createServer } from "node:http";

const server = createServer((req, res) => {
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
  res.end(`${req.method} ${req.url}\n`);
});

server.listen(3000);

req.method ist die Methode in Großbuchstaben, req.url der angefragte Pfad. Der Server oben antwortet mit nichts anderem als diesen beiden Angaben, du kannst ihn also fragen und nachlesen, was angekommen ist. Tipp eine andere Adresse in die Adresszeile, und der Pfad in der Antwort ändert sich mit.

Eine andere Methode bekommst du im Browser nicht hin, der schickt beim Eintippen immer GET. Im Terminal schon:

curl http://localhost:3000/buecher

curl -X POST http://localhost:3000/buecher

curl -X DELETE http://localhost:3000/buecher/12

Zusammen ergeben die beiden Angaben die Frage, die dein Server beantworten muss, und diese Frage hat immer zwei Teile: GET /buecher und POST /buecher sind zwei völlig verschiedene Dinge am selben Ort. Das eine holt die Liste, das andere legt einen Eintrag an. Wer nur auf den Pfad schaut, baut sich einen Server, der beim Anlegen die Liste zurückgibt.

req.url ist nicht die ganze Adresse

Der Name führt in die Irre. In req.url steht nicht die Adresse, die der Nutzer eingetippt hat, sondern nur alles ab dem Hostnamen.

Was in req.url wirklich steht
import { createServer } from "node:http";

const server = createServer((req, res) => {
  const adresse = new URL(req.url, "http://localhost");

  res.setHeader("Content-Type", "text/plain; charset=utf-8");
  res.end(
    [
      `req.url ist: ${req.url}`,
      `pathname:    ${adresse.pathname}`,
      `q:           ${adresse.searchParams.get("q")}`,
      `seite:       ${adresse.searchParams.get("seite")}`,
    ].join("\n") + "\n"
  );
});

server.listen(3000);

Der Browser steht hier gleich auf /suche?q=kaffee&seite=2, und in der ersten Zeile der Antwort siehst du, was davon in req.url ankommt. Der Hostname fehlt, das Protokoll fehlt, und der Grund dafür ist einleuchtend: Die Anfrage ist ja schon bei dir angekommen. Wo sie hin sollte, weiß dein Server, sonst hätte er sie nicht.

Zum Zerlegen nimmst du new URL(req.url, "http://localhost"). Das zweite Argument ist eine Basis, die URL braucht, weil der erste Teil allein keine vollständige Adresse ist. Welchen Hostnamen du dort hinschreibst, ist völlig egal, solange du danach nur pathname und searchParams benutzt.

Der handgestrickte Weg wäre req.url.split("?")[0], und für den Pfad allein funktioniert er sogar. Sobald aber Werte aus der Adresszeile dazukommen, hört es auf: Kodierung, Mehrfachwerte, leere Werte, Sonderzeichen. Das ist Lektion 8.6, und die Kurzfassung lautet, dass URL das alles schon richtig macht.

Kopfzeilen

req.headers ist ein ganz normales Objekt mit den Kopfzeilen der Anfrage.

Kopfzeilennamen sind kleingeschrieben
import { createServer } from "node:http";

const server = createServer((req, res) => {
  const zeilen = Object.entries(req.headers).map(
    ([name, wert]) => `${name} = ${wert}`
  );

  res.setHeader("Content-Type", "text/plain; charset=utf-8");
  res.end(zeilen.join("\n") + "\n");
});

server.listen(3000);

Rechts steht, was dein Browser gerade mitgeschickt hat. Schick dir selbst eine Zeile dazu, und zwar mit einem Großbuchstaben darin:

curl -H "X-Test: Hallo" http://localhost:3000/

In der Antwort steht x-test = Hallo. Die Namen sind immer kleingeschrieben, egal wie der Aufrufer sie geschrieben hat. Das ist eine Zusage von Node und keine Zufälligkeit, auf die du dich verlassen kannst: req.headers["x-test"] findet den Wert, req.headers["X-Test"] ist undefined. Die Werte lässt Node dagegen so, wie sie ankamen.

Die Handvoll, die dir im Alltag wirklich begegnet:

  • content-type sagt, was im Inhalt der Anfrage steckt. Wichtig ab Abschnitt 10, wenn du Formulare und JSON entgegennimmst.
  • accept sagt, was der Aufrufer gern hätte.
  • user-agent sagt, welches Programm anfragt.
  • cookie trägt die Cookies, die du selbst vorher gesetzt hast. Das ist Lektion 13.3.
  • authorization trägt Anmeldedaten.

Und jetzt der Satz, der bleiben soll

Nichts davon darfst du glauben. Jede Kopfzeile kommt vom Aufrufer, und ein Aufrufer muss kein Browser sein. Du hast dir gerade selbst eine ausgedacht und mitgeschickt. Genauso frei lässt sich user-agent, accept oder authorization erfinden, probier es aus:

curl -H "User-Agent: ich bin ein Browser, ehrlich" http://localhost:3000/

Das klingt selbstverständlich, wird aber ständig vergessen. Wer die Sprache aus accept-language liest, um eine Übersetzung zu wählen, macht alles richtig: Im schlimmsten Fall steht die Seite auf Finnisch da. Wer aus einer Kopfzeile liest, wer der Nutzer ist, hat ein Problem, denn diese Zeile kann sich jeder selbst schreiben.

Dieser Satz gehört hierher und nicht erst in Abschnitt 15, weil du ab der nächsten Lektion anfängst, mit diesen Werten zu arbeiten. Merk ihn dir gleich mit: Alles aus req ist eine Behauptung, keine Tatsache.

Zum Mitnehmen

Methode und Pfad sind die zwei Angaben, aus denen jedes Routing entsteht. Und alles, was in einer Kopfzeile steht, hat sich der Aufrufer ausgedacht.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.