Menu

Middleware w Express: jak działa next(), z przykładami

Middleware w Express to funkcja (req, res, next), która wykonuje się przed handlerem trasy. Może odczytać lub zmienić żądanie, zakończyć je odpowiedzią albo wywołać next(), żeby przekazać je dalej.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

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() albo res.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

PiszeszDział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.

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

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

Zacznij