Menu

Middleware ב-Express: איך next() עובד, עם דוגמאות

middleware ב-Express הוא פונקציה (req, res, next) שרצה לפני ה-route handler. הוא יכול לקרוא או לשנות את הבקשה, לסיים אותה עם תשובה, או לקרוא ל-next() כדי להעביר אותה הלאה.

בדף הזה יש עורכים שאפשר להריץ. לערוך, להריץ ולראות את הפלט מיד.

middleware ב-Express הוא פונקציה עם החתימה (req, res, next) שרצה בין הרגע שבו בקשה מגיעה לרגע שבו נשלחת תשובה. כל middleware יכול לקרוא או לשנות את req ואת res, לסיים את הבקשה בשליחת תשובה, או לקרוא ל-next() כדי להעביר את הבקשה לפונקציה הבאה בתור. app.use(fn) מוסיף middleware לכל בקשה, ו-Express מריץ הכל לפי הסדר שבו רשמתם אותו.

ה-Console מדפיס את שלושת השלבים לפי הסדר בכל בקשה, וה-route קורא את req.startedAt, מאפיין שה-logger הוסיף. שלחו GET /missing: שני ה-middleware עדיין מדפיסים, כי app.use() תואם לכל נתיב, ואז Express עונה 404 כי אף route לא התאים.

לשכוח את next()

middleware שלא שולח תשובה ולא קורא ל-next() משאיר את הבקשה תלויה. הלקוח מחכה עד ל-timeout, ושום דבר לא מופיע בלוגים, וזה הופך את זה לבאג middleware נפוץ. מחקו את next(); מ-logger שלמעלה ושלחו שוב GET /: הבקשה לא מקבלת תשובה אף פעם.

הכלל פשוט: כל מסלול בתוך middleware מסתיים בדיוק באחד מאלה:

גם קריאה ל-next() אחרי שליחת תשובה היא באג. ה-handler הבא מנסה לשלוח תשובה שנייה ו-Express זורק ERR_HTTP_HEADERS_SENT. כתבו return res.status(401).json(...) כדי שהפונקציה תיעצר שם.

בדיקת הרשאה שעוצרת את הבקשה

middleware שדוחה בקשה שולח את התשובה בעצמו ואף פעם לא קורא ל-next(). העבירו אותו כארגומנט נוסף ל-route כדי להגן רק על ה-route הזה:

הבקשה הראשונה מקבלת 401 עם {"error":"Missing or wrong API key"}. הוסיפו בשורת הבקשה את ה-header ‏x-api-key: secret123 ושלחו שוב: עכשיו התשובה היא {"users":42,"caller":"reporting-app"}. ‏/public אף פעם לא מריץ את הבדיקה.

איפה middleware רץ

מה כתבתםרץ עבור
app.use(fn)כל בקשה, כל method
app.use('/api', fn)נתיבים שמתחילים ב-/api
app.get('/admin', fn, handler)רק GET ל-/admin, לפני handler
router.use(fn)כל route של ה-router הזה

הסדר הוא חלק מהמשמעות. logger שנרשם אחרי ה-routes אף פעם לא ירשום בקשות שה-routes האלה ענו עליהן, כי route ששולח תשובה לא קורא ל-next(). רשמו קודם middleware לכל האפליקציה, כמו לוגים, פענוח גוף ו-CORS, אחר כך את ה-routes, ואחר כך את ה-handlers של 404 ושל שגיאות.

Middleware מובנים

Express מגיע עם כמה פונקציות middleware שמוסיפים עם app.use(). ה-body parsers ממלאים את req.body; בלעדיהם הוא undefined.

Middlewareמה הוא עושה
express.json()מפענח גופי JSON (‏Content-Type: application/json)
express.urlencoded()מפענח שליחות של טפסי HTML (‏name=Ada&age=36)
express.text()שם גוף של טקסט רגיל ב-req.body כמחרוזת
express.static('public')מגיש קבצים מתיקייה

שלחו POST /echo עם הגוף {"name": "Ada"} והתשובה כוללת "body":{"name":"Ada"}. הסירו את השורה של express.json(), שלחו שוב, ו-body נעלם מהתשובה: req.body הוא undefined, ו-JSON.stringify משמיט ערכים שהם undefined. גם חבילות כמו cors, helmet ו-morgan הן middleware: מתקינים אותן עם npm install ומעבירים אותן ל-app.use() באותה דרך. הדף על express.static מכסה הגשת קבצים.

טעויות נפוצות

לקרוא ל-middleware במקום להעביר אותו. app.use(logger()) מריץ את logger פעם אחת בעלייה ורושם את הערך שהוא מחזיר. כתבו app.use(logger). (ל-express.json() יש סוגריים כי הוא מחזיר פונקציית middleware.)

להוסיף את ה-body parser אחרי ה-routes. ה-routes רצים קודם ורואים את req.body כ-undefined.

לצפות ש-app.use('/admin', fn) יתאים לנתיב אחד. הוא רץ גם ל-/admin/users ולכל נתיב אחר מתחת ל-/admin, לכל method. כדי להגן על route בודד, העבירו את ה-middleware ל-app.get('/admin', fn, handler).

שאלות נפוצות

מה ההבדל בין middleware ל-route handler?

טכנית אין הבדל: שניהם פונקציות של (req, res, next). לפי מוסכמה, route handler מסיים את הבקשה עם תשובה, ו-middleware בדרך כלל עושה עבודה משותפת וקורא ל-next().

האם middleware יכול להיות async?

כן. ב-Express 5, ‏middleware אסינכרוני שזורק או מחזיר promise שנדחה שולח את השגיאה ל-error handler שלכם אוטומטית. עשו await למה שאתם צריכים, ואז קראו ל-next().

מה עושה next('route')?

הוא מדלג על שאר ה-handlers של ה-route הנוכחי ועובר ל-route הבא שתואם. זה עובד רק ב-handlers שנוספו עם app.get(), app.post() ושאר פונקציות ה-methods, לא ב-app.use().

איך מדלגים על middleware עבור route אחד?

רשמו את ה-route הזה לפני ה-middleware, כך שהוא יענה קודם, או בדקו את הנתיב בתוך ה-middleware (if (req.path === '/health') return next();). הפעלת ה-middleware לכל router או לכל route בנפרד, במקום לכל האפליקציה, הרבה פעמים מייתרת את הצורך.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל