Express'te middleware, bir isteğin geldiği an ile bir response'un gönderildiği an arasında çalışan, (req, res, next) imzalı bir fonksiyondur. Her middleware req ve res nesnelerini okuyabilir ya da değiştirebilir, bir response göndererek isteği bitirebilir ya da isteği sıradaki fonksiyona vermek için next() çağırabilir. app.use(fn) her istek için bir middleware ekler ve Express her şeyi kaydettiğin sırayla çalıştırır.
Console her istek için üç adımı sırayla yazar ve route, logger'ın eklediği bir property olan req.startedAt değerini okur. GET /missing gönder: iki middleware yine log yazar, çünkü app.use() her path ile eşleşir, sonra hiçbir route eşleşmediği için Express 404 ile cevap verir.
next() çağırmayı unutmak
Ne response gönderen ne de next() çağıran bir middleware isteği asılı bırakır. İstemci zaman aşımına kadar bekler ve log'larda hiçbir şey görünmez, bu da onu yaygın bir middleware hatası yapar. Yukarıdaki logger fonksiyonundan next(); satırını sil ve GET / isteğini tekrar gönder: istek hiç cevap almaz.
Kural basit: bir middleware içindeki her yol bunlardan tam olarak biriyle biter:
- devam etmek için
next(), - hata handler'ına atlamak için
next(err)(bkz. Express error handling), res.json()ya dares.status(401).send()gibi bir response.
Bir response gönderdikten sonra next() çağırmak da bir hatadır. Sıradaki handler ikinci bir response göndermeye çalışır ve Express ERR_HTTP_HEADERS_SENT fırlatır. Fonksiyon orada dursun diye return res.status(401).json(...) yaz.
İsteği durduran bir auth kontrolü
Bir isteği reddeden middleware response'u kendisi gönderir ve asla next() çağırmaz. Yalnızca o route'u korumak için onu route'a ek bir argüman olarak ver:
İlk istek {"error":"Missing or wrong API key"} ile 401 alır. İstek çubuğuna x-api-key: secret123 header'ını ekle ve tekrar gönder: bu sefer cevap {"users":42,"caller":"reporting-app"} olur. /public kontrolü hiç çalıştırmaz.
Middleware nerede çalışır
| Yazdığın | Ne için çalışır |
|---|---|
app.use(fn) | Her istek, her metot |
app.use('/api', fn) | /api ile başlayan path'ler |
app.get('/admin', fn, handler) | Yalnızca GET /admin, handler öncesinde |
router.use(fn) | O router'ın her route'u |
Sıra anlamın bir parçasıdır. Route'lardan sonra kaydedilen bir logger, o route'ların cevapladığı istekleri hiç log'a yazmaz, çünkü response gönderen bir route next() çağırmaz. Logging, body parsing ve CORS gibi uygulama geneli middleware'i önce kaydet, sonra route'ları, en son da 404 ve hata handler'larını.
Yerleşik middleware
Express, app.use() ile ekleyebileceğin birkaç middleware fonksiyonuyla gelir. Body parser'lar req.body değerini doldurur; onlar olmadan bu değer undefined olur.
| Middleware | Ne yapar |
|---|---|
express.json() | JSON body'leri parse eder (Content-Type: application/json) |
express.urlencoded() | HTML form gönderimlerini parse eder (name=Ada&age=36) |
express.text() | Düz metin body'yi string olarak req.body içine koyar |
express.static('public') | Bir klasördeki dosyaları sunar |
{"name": "Ada"} body'siyle POST /echo gönder, cevap "body":{"name":"Ada"} içerir. express.json() satırını sil, tekrar gönder ve cevapta body olmadığını gör: req.body değeri undefined olur ve JSON.stringify undefined değerleri dışarıda bırakır. cors, helmet ve morgan gibi paketler de middleware'dir: onları npm install ile kurarsın ve aynı şekilde app.use() fonksiyonuna verirsin. Dosya sunmayı express.static sayfası anlatıyor.
Sık yapılan hatalar
Middleware'i vermek yerine çağırmak. app.use(logger()), logger fonksiyonunu açılışta bir kez çalıştırır ve dönüş değerini kaydeder. app.use(logger) yaz. (express.json() parantezli yazılır, çünkü bir middleware fonksiyonu döndürür.)
Body parser'ı route'lardan sonra eklemek. Route'lar önce çalışır ve req.body değerini undefined olarak görür.
app.use('/admin', fn) ifadesinin tek bir path ile eşleşmesini beklemek. /admin/users ve /admin altındaki diğer her path için, her metotta da çalışır. Tek bir route'u korumak için middleware'i app.get('/admin', fn, handler) şeklinde ver.
Sıkça Sorulan Sorular
Middleware ile route handler arasındaki fark nedir?
Teknik olarak hiçbir şey: ikisi de (req, res, next) fonksiyonudur. Geleneksel olarak route handler isteği bir response ile bitirir, middleware ise genelde ortak bir iş yapar ve next() çağırır.
Middleware async olabilir mi?
Evet. Express 5'te hata fırlatan ya da reject edilmiş bir promise döndüren async bir middleware, hatayı otomatik olarak hata handler'ına gönderir. İhtiyacın olanı await et, sonra next() çağır.
next('route') ne yapar?
Mevcut route'un kalan handler'larını atlar ve eşleşen bir sonraki route'a geçer. Yalnızca app.get(), app.post() ve diğer metot fonksiyonlarıyla eklenen handler'larda çalışır, app.use() içinde çalışmaz.
Bir route için bir middleware'i nasıl atlarım?
O route'u middleware'den önce kaydet ki önce o cevap versin, ya da middleware'in içinde path'i kontrol et (if (req.path === '/health') return next();). Middleware'i tüm uygulama yerine router ya da route bazında uygulamak çoğu zaman bu ihtiyacı ortadan kaldırır.