Middleware w Express to funkcja o sygnaturze (req, res, next), która działa między chwilą, w której przychodzi żądanie, a chwilą wysłania odpowiedzi. Każdy middleware może odczytać lub zmienić req i res, zakończyć żądanie, wysyłając odpowiedź, albo wywołać next(), żeby przekazać żądanie kolejnej funkcji w kolejce. app.use(fn) dodaje middleware dla każdego żądania, a Express wykonuje wszystko w kolejności rejestracji.
Console przy każdym żądaniu wypisuje trzy kroki po kolei, a trasa odczytuje req.startedAt, właściwość dodaną przez logger. Wyślij GET /missing: oba middleware i tak logują, bo app.use() pasuje do każdej ścieżki, a potem Express odpowiada 404, bo żadna trasa nie pasowała.
Zapomniane next()
Middleware, który ani nie wysyła odpowiedzi, ani nie wywołuje next(), zostawia żądanie zawieszone. Klient czeka do timeoutu, a w logach nic się nie pojawia, dlatego to częsty błąd w middleware. Usuń next(); z logger powyżej i wyślij jeszcze raz GET /: żądanie nigdy nie dostanie odpowiedzi.
Zasada jest prosta: każda ścieżka przez middleware kończy się dokładnie jednym z tych wywołań:
next(), żeby iść dalej,next(err), żeby przeskoczyć do error handlera (zobacz obsługę błędów w Express),- odpowiedzią, np.
res.json()albores.status(401).send().
Wywołanie next() po wysłaniu odpowiedzi to też błąd. Następny handler próbuje wysłać drugą odpowiedź, a Express rzuca ERR_HTTP_HEADERS_SENT. Pisz return res.status(401).json(...), żeby funkcja się tam zatrzymała.
Sprawdzanie autoryzacji, które zatrzymuje żądanie
Middleware, który odrzuca żądanie, sam wysyła odpowiedź i nigdy nie wywołuje next(). Przekaż go trasie jako dodatkowy argument, żeby chronić tylko tę trasę:
Pierwsze żądanie dostaje 401 z {"error":"Missing or wrong API key"}. Dodaj w pasku żądania nagłówek x-api-key: secret123 i wyślij jeszcze raz: teraz odpowiedź to {"users":42,"caller":"reporting-app"}. /public nigdy nie uruchamia tego sprawdzenia.
Gdzie działa middleware
| Piszesz | Działa dla |
|---|---|
app.use(fn) | Każdego żądania, każdej metody |
app.use('/api', fn) | Ścieżek zaczynających się od /api |
app.get('/admin', fn, handler) | Tylko GET /admin, przed handler |
router.use(fn) | Każdej trasy tego routera |
Kolejność jest częścią znaczenia. Logger zarejestrowany po trasach nigdy nie zaloguje żądań, na które te trasy odpowiedziały, bo trasa, która wysyła odpowiedź, nie wywołuje next(). Najpierw rejestruj middleware dla całej aplikacji, np. logowanie, parsowanie body i CORS, potem trasy, a na końcu handlery 404 i błędów.
Wbudowane middleware
Express ma kilka funkcji middleware, które dodajesz przez app.use(). Parsery body wypełniają req.body; bez nich jest undefined.
| Middleware | Co robi |
|---|---|
express.json() | Parsuje body w JSON (Content-Type: application/json) |
express.urlencoded() | Parsuje formularze HTML (name=Ada&age=36) |
express.text() | Wkłada body w postaci tekstu do req.body jako string |
express.static('public') | Serwuje pliki z folderu |
Wyślij POST /echo z body {"name": "Ada"}, a odpowiedź zawiera "body":{"name":"Ada"}. Usuń linię express.json(), wyślij jeszcze raz, a body zniknie z odpowiedzi: req.body to undefined, a JSON.stringify pomija wartości undefined. Pakiety takie jak cors, helmet i morgan to też middleware: instalujesz je przez npm install i przekazujesz do app.use() w ten sam sposób. Serwowanie plików opisuje strona o express.static.
Częste błędy
Wywoływanie middleware zamiast przekazywania go. app.use(logger()) uruchamia logger raz przy starcie i rejestruje to, co zwrócił. Pisz app.use(logger). (express.json() ma nawiasy, bo zwraca funkcję middleware.)
Dodanie parsera body po trasach. Trasy wykonują się pierwsze i widzą req.body jako undefined.
Oczekiwanie, że app.use('/admin', fn) pasuje do jednej ścieżki. Działa też dla /admin/users i każdej innej ścieżki pod /admin, dla każdej metody. Żeby chronić pojedynczą trasę, przekaż middleware do app.get('/admin', fn, handler).
Najczęściej zadawane pytania
Czym różni się middleware od handlera trasy?
Technicznie niczym: oba to funkcje (req, res, next). Zgodnie z konwencją handler trasy kończy żądanie odpowiedzią, a middleware zwykle wykonuje wspólną pracę i wywołuje next().
Czy middleware może być async?
Tak. W Express 5 middleware async, który rzuci wyjątek albo zwróci odrzucony promise, automatycznie przekazuje błąd do twojego error handlera. Poczekaj przez await na to, czego potrzebujesz, i wywołaj next().
Co robi next('route')?
Pomija pozostałe handlery bieżącej trasy i przechodzi do następnej pasującej trasy. Działa tylko w handlerach dodanych przez app.get(), app.post() i inne funkcje metod, nie w app.use().
Jak pominąć middleware dla jednej trasy?
Zarejestruj tę trasę przed middleware, żeby odpowiedziała pierwsza, albo sprawdź ścieżkę w middleware (if (req.path === '/health') return next();). Często wystarczy podpiąć middleware do routera lub trasy zamiast do całej aplikacji.