Node.js / Express.js - app.router ทำงานอย่างไร


298

ก่อนที่ฉันจะถามเกี่ยวกับapp.routerฉันคิดว่าฉันควรอธิบายอย่างน้อยสิ่งที่ฉันคิดว่าเกิดขึ้นเมื่อทำงานกับมิดเดิลแวร์ app.use()กับการใช้ตัวกลางฟังก์ชั่นการใช้งาน เมื่อมีการดำเนินการมิดเดิลแวร์มันจะเรียกมิดเดิลแวร์ถัดไปโดยใช้next()หรือทำให้มิดเดิลแวร์ไม่ได้รับการเรียก นั่นหมายความว่าลำดับที่ฉันวางสายมิดเดิลแวร์ของฉันมีความสำคัญเพราะมิดเดิลแวร์บางตัวขึ้นอยู่กับมิดเดิลแวร์อื่นและมิดเดิ้ลบางอันใกล้จะสิ้นสุดอาจไม่ถูกเรียก

วันนี้ฉันทำงานกับแอปพลิเคชันของฉันและให้เซิร์ฟเวอร์ทำงานในพื้นหลัง ฉันต้องการเปลี่ยนแปลงและรีเฟรชหน้าเว็บและดูการเปลี่ยนแปลงในทันที โดยเฉพาะฉันทำการเปลี่ยนแปลงเค้าโครงของฉัน ฉันไม่สามารถทำงานได้ดังนั้นฉันจึงค้นหา Stack Overflow เพื่อหาคำตอบและพบคำถามนี้ มันบอกว่าเพื่อให้แน่ใจว่าเป็นใต้express.static() require('stylus')แต่เมื่อฉันดูรหัสของ OP ฉันเห็นว่าเขาได้รับapp.routerโทรศัพท์เมื่อโทรมิดเดิลแวร์สิ้นสุดและฉันพยายามหาสาเหตุว่าทำไม

เมื่อฉันสร้างแอปพลิเคชัน Express.js ของฉัน (เวอร์ชัน 3.0.0rc4) ฉันใช้คำสั่งexpress app --sessions --css stylusและในไฟล์ app.js รหัสของฉันได้รับการตั้งค่าโดยมีรหัสapp.routerข้างต้นทั้งสองexpress.static()และการrequire('stylus')โทร ดังนั้นดูเหมือนว่าถ้ามันมาพร้อมการติดตั้งแบบนั้นแล้วมันควรจะอยู่อย่างนั้น

หลังจากจัดเรียงรหัสของฉันอีกครั้งเพื่อให้ฉันเห็นการเปลี่ยนแปลง Stylus ของฉันดูเหมือนว่า:

app.configure(function(){
  //app.set() calls
  //app.use() calls
  //...
  app.use(app.router);
  app.use(require('stylus').middleware(__dirname + '/public'));
  app.use(express.static(__dirname + '/public', {maxAge: 31557600000}));
});

app.get('/', routes.index);

app.get('/test', function(req, res){
  res.send('Test');
});

ดังนั้นฉันจึงตัดสินใจว่าขั้นตอนแรกคือการค้นหาว่าทำไมมันถึงมีความสำคัญapp.routerในรหัสของฉันด้วยซ้ำ ดังนั้นผมจึงให้ความเห็นมันออกมาเริ่ม app /ของฉันและการสำรวจ มันแสดงหน้าดัชนีของฉันได้ดี อืมอาจจะใช้ได้เพราะฉันส่งออกเส้นทางจากไฟล์เส้นทางของฉัน (route.index) ดังนั้นต่อไปฉันไปที่/testและมันแสดงการทดสอบบนหน้าจอ ฮ่าฮ่าโอเคฉันไม่รู้ว่าapp.routerมันทำอะไร ไม่ว่ามันจะรวมอยู่ในรหัสของฉันหรือไม่เส้นทางของฉันก็โอเค ดังนั้นฉันขาดอะไรบางอย่างแน่นอน

ดังนั้นนี่คือคำถามของฉัน:

ใครช่วยกรุณาอธิบายสิ่งapp.routerที่สำคัญของมันและที่ฉันควรวางไว้ในมิดเดิลแวร์โทรของฉัน? นอกจากนี้ยังจะดีถ้าผมได้คำอธิบายสั้น ๆ express.static()เกี่ยวกับ เท่าที่ฉันสามารถบอกได้ว่าexpress.static()เป็นแคชของข้อมูลของฉันและหากแอปพลิเคชันไม่สามารถค้นหาหน้าที่ร้องขอได้มันจะตรวจสอบแคชเพื่อดูว่ามีอยู่หรือไม่


18
ขอบคุณที่ถามคำถามนี้ ฉันได้ลองค้นหาคำตอบนี้ (และคำถามเพื่อให้พร้อม)
Hari Seldon

8
นั่นเป็นคำถามที่เขียนได้ดีมากฉันกำลังทำสิ่งเดียวกัน
Kirn

คำตอบ:


329

หมายเหตุ:นี่อธิบายถึงวิธีการทำงานของ Express ในเวอร์ชัน 2 และ 3 โปรดดูที่ส่วนท้ายของโพสต์นี้สำหรับข้อมูลเกี่ยวกับ Express 4


staticเพียงแค่ให้บริการไฟล์ ( ทรัพยากรคงที่ ) จากดิสก์ คุณให้เส้นทาง (บางครั้งเรียกว่าจุดเชื่อมต่อ) และให้บริการไฟล์ในโฟลเดอร์นั้น

ตัวอย่างเช่นexpress.static('/var/www')จะให้บริการไฟล์ในโฟลเดอร์นั้น ดังนั้นร้องขอไปยังเซิร์ฟเวอร์โหนดของคุณสำหรับการจะทำหน้าที่http://server/file.html/var/www/file.html

routerเป็นรหัสที่รันเส้นทางของคุณ เมื่อคุณทำสิ่งapp.get('/user', function(req, res) { ... });นี้จะเป็นการrouterเรียกใช้ฟังก์ชันการเรียกกลับเพื่อประมวลผลคำขอ

ลำดับที่คุณส่งผ่านสิ่งต่าง ๆ เพื่อapp.useกำหนดลำดับที่มิดเดิลแวร์แต่ละตัวได้รับโอกาสในการประมวลผลคำขอ ตัวอย่างเช่นหากคุณมีไฟล์ชื่อtest.htmlในโฟลเดอร์สแตติกและเส้นทาง:

app.get('/test.html', function(req, res) {
    res.send('Hello from route handler');
});

อันไหนที่ถูกส่งไปยังลูกค้าร้องขอhttp://server/test.html? ใช้งานมิดเดิลแวร์เครื่องใดuseก่อน

หากคุณทำสิ่งนี้:

app.use(express.static(__dirname + '/public'));
app.use(app.router);

จากนั้นไฟล์บนดิสก์จะได้รับบริการ

หากคุณทำอย่างอื่น

app.use(app.router);
app.use(express.static(__dirname + '/public'));

จากนั้นตัวจัดการเส้นทางจะได้รับคำขอและ "Hello จากตัวจัดการเส้นทาง" จะถูกส่งไปยังเบราว์เซอร์

โดยปกติคุณต้องการวางเราเตอร์ไว้เหนือมิดเดิลแวร์แบบสแตติกเพื่อให้ไฟล์ที่มีชื่อโดยไม่ได้ตั้งใจไม่สามารถแทนที่หนึ่งในเส้นทางของคุณ

โปรดทราบว่าถ้าคุณทำไม่ได้อย่างชัดเจนนั้นจะถูกเพิ่มโดยปริยายโดยด่วนที่จุดที่คุณกำหนดเส้นทาง (ซึ่งเป็นเหตุผลที่เส้นทางของคุณยังคงทำงานแม้ว่าคุณจะออกความเห็น)userouterapp.use(app.router)


ผู้แสดงความคิดเห็นได้พูดถึงจุดต่าง ๆ เกี่ยวกับลำดับstaticและrouterฉันไม่ได้พูดถึง: ผลกระทบต่อประสิทธิภาพโดยรวมของแอปของคุณ

เหตุผลไปยังอีกuse routerข้างต้นstaticคือการเพิ่มประสิทธิภาพการปฏิบัติงาน หากคุณใส่staticก่อนคุณจะต้องกดฮาร์ดไดรฟ์ทุกคำขอเพื่อดูว่ามีไฟล์อยู่หรือไม่ ในการทดสอบอย่างรวดเร็วฉันพบว่าโอเวอร์เฮดนี้มีค่า ~ 1ms บนเซิร์ฟเวอร์ที่ไม่ได้โหลด (จำนวนนั้นมีแนวโน้มที่จะสูงกว่าโหลดมากซึ่งคำขอจะแย่งชิงการเข้าถึงดิสก์)

ในrouterตอนแรกคำร้องขอที่ตรงกับเส้นทางไม่จำเป็นต้องไปถึงดิสก์ช่วยประหยัดมิลลิวินาทีอันมีค่า

แน่นอนว่ามีวิธีในการลดstaticค่าใช้จ่าย

ตัวเลือกที่ดีที่สุดคือการวางทรัพยากรสแตติกทั้งหมดของคุณไว้ในโฟลเดอร์เฉพาะ (IE /static) จากนั้นคุณสามารถเมานต์staticกับพา ธ นั้นเพื่อให้ทำงานเฉพาะเมื่อเส้นทางเริ่มต้นด้วย/static:

app.use('/static', express.static(__dirname + '/static'));

routerในสถานการณ์เช่นนี้คุณต้องการใส่ข้างต้นนี้ วิธีนี้หลีกเลี่ยงการประมวลผลมิดเดิลแวร์ / เราเตอร์อื่น ๆ หากมีไฟล์อยู่ แต่จริงๆแล้วฉันสงสัยว่าคุณจะได้รับประโยชน์มากมาย

นอกจากนี้คุณยังสามารถใช้staticCacheซึ่งแคชทรัพยากรคงที่ในหน่วยความจำเพื่อให้คุณไม่ต้องกดดิสก์สำหรับไฟล์ที่ร้องขอทั่วไป ( คำเตือน: staticCache จะถูกลบออกในอนาคต)

อย่างไรก็ตามฉันไม่คิดว่าstaticCacheแคชคำตอบเชิงลบ (เมื่อไม่มีไฟล์) ดังนั้นจึงไม่ช่วยถ้าคุณใส่staticCacheด้านบนrouterโดยไม่ติดตั้งกับเส้นทาง

เช่นเดียวกับคำถามทั้งหมดเกี่ยวกับประสิทธิภาพการวัดและมาตรฐานแอพโลกแห่งความเป็นจริงของคุณ (ภายใต้ภาระงาน) เพื่อดูว่าคอขวดอยู่ตรงไหน


ด่วน 4

ด่วน 4.0 ลบ app.routerมิดเดิลแวร์ทั้งหมด ( app.use) และเส้นทาง ( app.getet al) ทั้งหมดจะถูกประมวลผลตามลำดับที่ถูกเพิ่มอย่างแม่นยำ

ในคำอื่น ๆ :

วิธีการกำหนดเส้นทางทั้งหมดจะถูกเพิ่มตามลำดับที่ปรากฏ คุณควรจะไม่app.use(app.router)ทำ สิ่งนี้จะช่วยขจัดปัญหาที่พบบ่อยที่สุดกับ Express

กล่าวอีกนัยหนึ่งคือผสมapp.use()และapp[VERB]()ทำงานอย่างถูกต้องตามลำดับที่ถูกเรียก

app.get('/', home);
app.use('/public', require('st')(process.cwd()));
app.get('/users', users.list);
app.post('/users', users.create);

อ่านเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงใน Express 4


2
routerไปในสถานที่หนึ่ง ถ้าครั้งแรกที่คุณโทรapp.get(หรือpostหรือคนอื่น ๆ ) คุณยังไม่ได้useวันที่app.routerด่วนเพิ่มมันสำหรับคุณ
josh3736

4
@MikeCauser: ไม่เพราะโอเวอร์เฮดของการเข้าถึงดิสก์ (เพื่อดูว่ามีไฟล์อยู่หรือไม่) มีขนาดใหญ่กว่าโอเวอร์เฮดของการเรียกใช้ฟังก์ชัน ในการทดสอบของฉันค่าใช้จ่ายนั้นมีค่า 1ms บนเซิร์ฟเวอร์ที่ไม่ได้โหลด เป็นไปได้สูงที่จะโหลดมากขึ้นซึ่งคำขอจะแย่งชิงการเข้าถึงดิสก์ ด้วยstaticหลังจากrouterคำถามเกี่ยวกับตัวกลางอื่น ๆ จะกลายเป็นที่ไม่เกี่ยวข้องเพราะมันจะต้องอยู่เหนือเราเตอร์
josh3736

2
คำอธิบายที่ยอดเยี่ยม! ขอบคุณมาก!
Kirn

3
app.routerจะถูกลบออกในสาขาหลักในปัจจุบันซึ่งจะมีการแสดง-4.0 แต่ละเส้นทางจะกลายเป็นมิดเดิ้ลแยกต่างหาก
yanychar

3
อีกหนึ่งความกระจ่างแจ้งขณะที่ฉันทำงานกับสิ่งนี้ ใน express 4 สามารถกำหนดเส้นทางหลายเส้นทางไปยังเราเตอร์และจากนั้นใช้เราเตอร์เราเตอร์จะได้รับเส้นทางรากและวางในสแต็ก "มิดเดิลแวร์" ผ่านทาง app.use (เส้นทางเราเตอร์) วิธีนี้จะช่วยให้เส้นทางที่เกี่ยวข้องกับแต่ละใช้เราเตอร์ของตัวเองและได้รับมอบหมายเส้นทางพื้นฐานเป็นหน่วย หากฉันเข้าใจดีขึ้นฉันจะเสนอให้โพสต์คำตอบอื่น อีกครั้งฉันได้รับสิ่งนี้จากscotch.io/tutorials/javascript/ …
Joe Lapp

2

การกำหนดเส้นทางหมายถึงการกำหนดวิธีที่แอปพลิเคชันตอบสนองต่อคำขอของลูกค้าไปยังปลายทางเฉพาะซึ่งเป็น URI (หรือเส้นทาง) และวิธีการร้องขอ HTTP เฉพาะ (GET, POST และอื่น ๆ ) แต่ละเส้นทางสามารถมีฟังก์ชั่นจัดการอย่างน้อยหนึ่งฟังก์ชั่นซึ่งจะทำงานเมื่อมีการจับคู่เส้นทาง

ใน Express 4.0 Router เราให้ความยืดหยุ่นมากกว่าที่เคยมีมาในการกำหนดเส้นทางของเรา

express.Router () ใช้หลายครั้งเพื่อกำหนดกลุ่มของเส้นทาง

เส้นทางที่ใช้เป็นมิดเดิลแวร์เพื่อประมวลผลคำขอ

เส้นทางที่ใช้เป็นมิดเดิลแวร์เพื่อตรวจสอบความถูกต้องของพารามิเตอร์โดยใช้ ".param ()"

app.route () ใช้เป็นทางลัดไปยังเราเตอร์เพื่อกำหนดหลายคำขอบนเส้นทาง

เมื่อเราใช้ app.route () เรากำลังเชื่อมต่อแอพของเรากับเราเตอร์นั้น

var express = require('express'); //used as middleware
var app = express(); //instance of express.
app.use(app.router);
app.use(express.static(__dirname + '/public')); //All Static like [css,js,images] files are coming from public folder
app.set('views',__dirname + '/views'); //To set Views
app.set('view engine', 'ejs'); //sets View-Engine as ejs
app.engine('html', require('ejs').renderFile); //actually rendering HTML files through EJS. 
app.get('/', function (req, res) {
  res.render('index');  
})
app.get('/test', function (req, res) {
  res.send('test')
})

0

ใน express Version 4 เราสามารถกำหนดเส้นทางได้อย่างง่ายดายในลักษณะดังต่อไปนี้:

server.js:

const express = require('express');
const app = express();
const route = require('./route');

app.use('/route', route);
// here we pass in the imported route object

app.listen(3000, () => console.log('Example app listening on port 3000!'));

route.js:

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

router.get('/specialRoute', function (req, res, next) {
     // route is now http://localhost:3000/route/specialRoute
});

router.get('/', function (req, res, next) {
    // route is now http://localhost:3000/route
});

module.exports = router;

ในserver.jsเรานำเข้าวัตถุเราเตอร์ของroute.jsไฟล์และนำไปใช้ในลักษณะต่อไปนี้ในserver.js:

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

ตอนนี้เส้นทางทั้งหมดในการroute.jsมี URL พื้นฐานดังต่อไปนี้:

http: // localhost: 3000 / เส้นทาง

ทำไมวิธีนี้:

ประโยชน์หลักของการใช้วิธีนี้คือว่าตอนนี้ app ของเรามีมากขึ้นแบบแยกส่วน ตัวจัดการเส้นทางทั้งหมดสำหรับเส้นทางบางเส้นทางในตอนนี้สามารถใส่ลงในไฟล์ต่าง ๆ ซึ่งทำให้ทุกอย่างสามารถบำรุงรักษาได้ง่ายขึ้นและหาได้ง่ายขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.