Abschnitt 10 · Lektion 6
Synthese: die eigene Kette
Fünf Lektionen, fünf Bausteine. Jetzt liegen sie alle auf dem Tisch, und die Frage ist nur noch: in welcher Reihenfolge?
Die sechs Glieder
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Buecherei</title>
</head>
<body>
<h1>Buecherei</h1>
</body>
</html> import express from "express";
const router = express.Router();
router.get("/buecher", (req, res) => res.json({ anzahl: 3 }));
router.post("/buecher", (req, res) => res.status(201).json({ titel: req.body?.titel }));
export default router; import express from "express";
import apiRouter from "./routen/api.js";
const app = express();
// 1. Protokoll ganz oben: sieht alles, auch was spaeter scheitert.
app.use((req, res, next) => {
res.on("finish", () => console.error(`${req.method} ${req.originalUrl} ${res.statusCode}`));
next();
});
// 2. Koerper lesen, bevor irgendeine Route ihn braucht.
app.use(express.json());
// 3. Statische Dateien: was hier liegt, braucht keine Route.
app.use(express.static("public"));
// 4. Die eigenen Routen.
app.use("/api", apiRouter);
// 5. Standardfall: greift, wenn bis hierhin niemand zustaendig war.
app.use((req, res) => res.status(404).json({ fehler: "nicht gefunden" }));
// 6. Fehler-Handler ganz unten, mit vier Argumenten.
app.use((fehler, req, res, next) => {
console.error(fehler.message);
res.status(500).json({ fehler: "Serverfehler" });
});
app.listen(3000); Rechts steht die Startseite aus public/, und niemand hat eine Route dafür geschrieben. /api/buecher
und /nix holst du über die Adresszeile, den POST über das Terminal:
curl -i -X POST -H "Content-Type: application/json" -d '{"titel":"Neu"}' http://localhost:3000/api/buecher
Diese Reihenfolge findest du in unzähligen Express-Projekten, und sie ist kein Zufall. Jede Position hat einen Grund.
Das Protokoll steht oben, weil es alles sehen soll. Auch die Anfragen, die gleich im 404 landen, und besonders die, die einen Fehler auslösen. Ein Protokollant weiter unten sieht nur noch das, was bis dorthin durchgekommen ist, und genau die Fälle, die dich interessieren, kommen nicht so weit.
Das Lesen des Bodys steht über den Routen, weil req.body sonst leer ist, wenn eine Route ihn
braucht. Du hast das in Lektion 10.3 gesehen, und es ist der häufigste Grund, warum eine POST-Route
partout nichts empfängt.
Statische Dateien stehen über den Routen, damit eine Datei nicht erst durch die ganze Routenlogik läuft, bevor sie ausgeliefert wird. Hier gilt aber die Ausnahme aus Lektion 9.5: Wenn eine Route dieselbe Adresse wie eine Datei bedienen soll, gewinnt, was oben steht. Dann drehst du die beiden Zeilen bewusst um und schreibst dir einen Kommentar dazu.
Die Routen stehen in der Mitte, weil sie die eigentliche Arbeit sind.
Der Standardfall steht unter allem, was antworten könnte. Er passt auf jede Anfrage, deshalb darf er erst dran sein, wenn wirklich niemand mehr zuständig war.
Der Fehler-Handler steht ganz unten, weil nur er von dort etwas entgegennehmen kann. Alles, was über ihm schiefgeht, landet bei ihm. Alles darunter gäbe es nicht.
Was passiert, wenn du eins verschiebst
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Buecherei</title>
</head>
<body>
<h1>Buecherei</h1>
</body>
</html> import express from "express";
const router = express.Router();
router.get("/buecher", (req, res) => res.json({ anzahl: 3 }));
export default router; import express from "express";
import apiRouter from "./routen/api.js";
const app = express();
// Der Standardfall steht zu weit oben. Er passt auf jede Anfrage, also ist ab
// hier alles ein 404, und niemand merkt es an einer Fehlermeldung.
app.use((req, res) => res.status(404).json({ fehler: "nicht gefunden" }));
app.use(express.static("public"));
app.use("/api", apiRouter);
app.listen(3000); Dieselbe Anwendung, eine Zeile verschoben. Rechts steht jetzt kein <h1>Buecherei</h1> mehr,
sondern ein 404, und /api/buecher gibt denselben. Kein Absturz, keine Warnung, keine
Fehlermeldung. Der Server läuft, er tut nur nichts mehr.
Das ist die Sorte Fehler, die einen Nachmittag kostet, wenn man das Prinzip nicht kennt, und dreißig Sekunden, wenn man es kennt. Wenn plötzlich alles einen 404 gibt, obwohl die Routen richtig aussehen, steht etwas über ihnen, das nicht dort hingehört.
Eine hängende Anfrage finden
Der zweite typische Fall ist der aus Lektion 10.1: Es kommt gar keine Antwort.
import express from "express";
const app = express();
// Wenn eine Anfrage haengt, protokollierst du jedes Glied einzeln. Die letzte
// Zeile, die noch kommt, steht direkt ueber der Stelle mit dem Fehler.
const protokoll = [];
// Diese Route steht ganz oben, damit du das Protokoll auch dann noch
// abrufen kannst, wenn die Kette darunter haengt.
app.get("/protokoll", (req, res) => res.json(protokoll));
app.use((req, res, next) => {
protokoll.push("1 Protokoll");
next();
});
app.use((req, res, next) => {
protokoll.push("2 Koerper lesen");
next();
});
app.use((req, res, next) => {
protokoll.push("3 Anmeldung pruefen");
// Hier fehlt next(). Weiter kommt nichts.
});
app.use((req, res, next) => {
protokoll.push("4 Routen");
next();
});
app.listen(3000); Die Methode ist unspektakulär und funktioniert immer: Schreib in jedes Glied eine Ausgabe mit einer Nummer und schick eine Anfrage. Die letzte Zeile, die noch erscheint, steht direkt über der Stelle, an der es klemmt.
Im Reiter „Browser” hängt die Anfrage auf /daten, die Seite lädt und lädt. Tipp jetzt /protokoll
in die Adresszeile: Dort steht die Liste, und sie hört nach Glied 3 auf. Genau dort fehlt das
next(), Glied 4 wird nie erreicht.
Die Route für das Protokoll steht in diesem Beispiel ganz oben, sonst hinge sie mit. In einer
echten Anwendung schreibst du statt einer Liste einfach console.log und schaust ins Terminal.
Dieselbe Technik hilft übrigens auch bei dem Fall darüber. Kommt eine Zeile nicht mehr, weil weiter oben schon geantwortet wurde, siehst du das an derselben Nummernfolge.
Zwei Fragen, die dir die Position verraten
Wenn du eine neue Middleware einsortierst, reichen meistens zwei Fragen.
Was muss vorher schon passiert sein? Eine Middleware, die req.body liest, gehört unter
express.json(). Eine, die die Anmeldung prüft, gehört über die Routen, die sie schützen soll.
Soll sie auch dann noch laufen, wenn schon geantwortet wurde? Wenn ja, kann sie nicht in der
Kette stehen, sondern gehört in ein res.on("finish", ...). Das ist der Grund, warum die
Zeitmessung aus Lektion 10.2 zweigeteilt ist.
In der Challenge baust du die Kette komplett. Zwei Glieder schreibst du selbst, den Rest hängst du ein, und die Reihenfolge ist die eigentliche Aufgabe.
Zum Mitnehmen
Sechs Glieder, und jede Position hat einen Grund. Protokoll oben, Fehler-Handler unten, Standardfall knapp darüber.
Jetzt du
Basis Konto, kostenlosZu 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.