express.js – ראוטר בסיסי

יצירת ראוטינג בסיסי ב-express במודול עצמאי
express.js

במאמר הקודם למדנו איך עושים אפליקצית hello world ואף למדנו על ראוטינג מאוד בסיסי כשלמדנו איך יוצרים moshe/ באפליקציה שלנו. היום נלמד על אחת החוזקות הגדולות של express – ראוטינג. בגדול ראוטינג (באנגלית routing) מאפשר לנו להעביר את הבקשות כרצוננו ולהחזיר את המידע.

הנה הקוד שעבדנו עליו במאמר הקודם:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.get('/', function (req, res) {
  res.send('Hello World!');
});

כאן אנו מטרגטים את כל בקשות ה-GET (כלומר הבקשות הרגילות), אם אנחנו רוצים ליצור API מסודר, אנחנו יכולים ליצור גם מתודות שונות שיעבדו לפי הבקשה – למשל ראוטר שיעבוד לפי בקשת POST:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.post('/', function (req, res) {
  res.send('POST to Hello World!');
});

אם אני אפתח את הדפדפן ב localhost:3000, אני אראה עדיין את ה-hello world, אבל אם אשגר בקשת POST ל-localhost:3000, אני אראה POST to Hello World. הנה הדוגמה. בקשת ה-POST נעשתה עם התוסף Advanced REST client שמאפשר משלוח בקשות POST בקלות. אם POST או GET נשמע לכם כמו סינית עתיקת יומין, קראו את המדריך הקצרצר ל-REST.

post example to express

יש כמובן מתודה לכל בקשה שהיא, כולל DELETE או PUT או אפילו בקשות שאתם מגדירים.

אם אני רוצה לשלוט על כל הבקשות ולקבל את כולן, אני יכול להשתמש ב-all. למשל:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.all('/', function (req, res) {
  res.send('Hello World!');
});


כל בקשה, REST או POST, תחזיק hello world. אני יודע שזה נשמע איזוטרי (מי לכל הרוחות יצטרך אותה תגובה גם ל-POST וגם ל-REST) אבל חשיבות ה-all תתברר בהמשך.

במקום לכתוב נתיבים ספציפיים, אני יכול להשתמש בביטויים רגולריים. למשל הקוד הבא:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.get('*', function (req, res) {
  res.send('Hello World!');
});


יתפוס את כל בקשות ה-GET ל: localhost:3000, בלי שום קשר לנתיב. גם אם אני אכניס http://localhost:3000/moshe וגם אם אני אכניס http://localhost:3000/bulbulhakabulvul/. אני יכול להכניס כל ביטוי רגולרי שהוא. למשל:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.get('/ab*cd', function(req, res) {
  res.send('Match abcd, abxcd, abRANDOMcd, ab123cd, and so on.');
});

א-מ-מ-ה? יש כאן פוטנציאל קטן להסתבכות – בואו תסתכלו על הקוד הבא:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.get('*', function (req, res) {
  res.send('Hello World!');
});

app.get('/ab*cd', function(req, res) {
  res.send('Match abcd, abxcd, abRANDOMcd, ab123cd, and so on.');
});

אם אני אכניס: http://localhost:3000/abcd, הקוד שלמעלה יתפוס ואני אקבל hello world במקום את הטקסט "Match abcd, abxcd, abRANDOMcd, ab123cd, and so on.". למה? כי express לא בודק את כל הנתיבים ואז מחליט מה יותר תואם. ברגע שיש התאמה הוא נכנס לפעולה. וכיוון שהביטוי * רלוונטי לכל הנתיבים, הוא יתפוס תמיד ומייד והקוד של


app.get('/ab*cd', function(req, res) {
  res.send('Match abcd, abxcd, abRANDOMcd, ab123cd, and so on.');
});

לעולם לא ירוץ. מה הפתרון במקרה הזה? לעשות את זה מהפרטי לכללי – ככה:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});


app.get('/ab*cd', function(req, res) {
  res.send('Match abcd, abxcd, abRANDOMcd, ab123cd, and so on.');
});

app.get('*', function (req, res) {
  res.send('Hello World!');
});

יפה ונחמד, אבל מה? לא ישים אם נצא קצת מהדוגמה ונגיע לכתיבת אפליקציה/אתר אמיתיים. למה? כי באפליקציה אמיתית יש לי אינספור נתיבים ואם אני אשפוך את כולם על קובץ אחד, יהיה לי סלט. בדיוק בשביל זה אני יכול ליצור ראוטר. הראוטר הוא מודול שמסדר לנו את הנתיבים באופן מסודר יותר ומובנה יותר.

בואו ונניח שבאפליקציה שלי אני רוצה שיהיה לי חלק שמוקדש ל-help, לכאורה, אני יכול לעשות משהו כזה:


var express = require('express');
var app = express();

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.get('/help/about-me', function(req, res) {
  res.send('About me page');
});

app.get('/help/how-to-use', function (req, res) {
  res.send('How to use');
});

לכאורה מעולה, אבל תחשבו על אתר שיש לו אפילו רק עשרות נתיבים ולכל אחד POST ו-GET ו-DELETE ו-PUT, הבלגן יהיה אדיר והקוד יהיה רב. במקום זה, עדיף לעשות משהו מודולרי יותר. למשל, ליצור קובץ חדש שנקרא help.js שהוא יהיה המודול ראוטר שלי. הקובץ יראה כך:


var express = require('express');
var router = express.Router();

router.get('/about-me', function(req, res) {
  res.send('About me page');
});

router.get('/how-to-use', function (req, res) {
  res.send('How to use');
});

module.exports = router;

מה יש לנו כאן? לא להבהל, זה די פשוט. קודם כל, אנחנו מייצרים instance של router, מדובר במתודה של express שמחזירה instance של ראוטר, שם אנו יוצרים את הנתיבים היחסיים ל-help.

ה-app.js שלי יראה כך:


var express = require('express');
var app = express();
var help = require('./help');

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

app.use('/help', help);

איך זה עובד? יצרנו מודול של help. כאשר מישהו נכנס לנתיב של help, מייד המודול הזה מופעל על ידי use. כל הבקשות מנותבות למודול ראוטר שלנו. המודול ראוטר שלנו מקבל את הכל ואז מנתב אותם כרגיל – רק תוך כדי שימוש ב-router ולא ב-app כמו בקובץ ה-app.js. בגדול, אם בקובץ הראשי שלנו אנחנו משתמשים בinstance של express (שנכנס לתוך משתנה של app), אז בראוטר שלנו אנחנו משתמשים ב-instance של ראוטר.

אולי הציור המדהים הזה שהכנתי יכול לסייע בהבנה:

express route scheme

בגדול, אין כאן הבדל גדול, זה רק עניין של סידור קוד. כדי שלא הכל יישפך ב-app.js המרכזי. וגם כדי שיהיה לנו קל להכניס middleware. רגע, מה? על כך במאמר הבא.

פוסטים נוספים שכדאי לקרוא

גלילה לראש העמוד