Express next function มีไว้ทำอะไร?


124

พยายามหาคำอธิบายที่ดีว่าnext()วิธีนี้ทำอย่างไร ในเอกสาร Express ระบุว่าnext('route')สามารถใช้เพื่อข้ามไปยังเส้นทางนั้นและข้ามทุกเส้นทางระหว่างกันได้ แต่บางครั้งก็nextถูกเรียกโดยไม่มีข้อโต้แย้ง ใครรู้วิธีการสอนที่ดีและอื่น ๆ ที่อธิบายถึงnextฟังก์ชั่น?

คำตอบ:


161

next()โดยไม่มีข้อโต้แย้งว่า "ล้อเล่นฉันไม่ต้องการจัดการเรื่องนี้จริงๆ" มันกลับเข้าไปและพยายามหาเส้นทางต่อไปที่จะตรงกัน

สิ่งนี้มีประโยชน์เช่นหากคุณต้องการมีตัวจัดการเพจบางประเภทที่มี url slugs รวมถึงสิ่งอื่น ๆ อีกมากมาย แต่นี่คือตัวอย่าง

app.get('/:pageslug', function(req, res, next){
  var page = db.findPage(req.params.pageslug);
  if (page) {
    res.send(page.body);
  } else {
    next();
  }
});

app.get('/other_routes', function() {
  //...
});

โค้ดที่สร้างขึ้นควรตรวจสอบฐานข้อมูลสำหรับหน้าที่มี id slug ถ้าเจอก็เรนเดอร์! หากไม่พบตัวจัดการเส้นทางนี้และตรวจสอบตัวจัดการเส้นทางอื่น

ดังนั้นการnext()ไม่มีข้อโต้แย้งใด ๆ ให้แสร้งทำเป็นว่าคุณไม่ได้จัดการเส้นทางเพื่อให้สิ่งอื่นมารับแทน


app.all('*')หรือเคาน์เตอร์ตีด้วย ซึ่งช่วยให้คุณสามารถรันโค้ดการตั้งค่าที่ใช้ร่วมกันจากนั้นไปยังเส้นทางอื่นเพื่อทำบางสิ่งที่เฉพาะเจาะจงมากขึ้น

app.all('*', function(req, res, next){
  myHitCounter.count += 1;
  next();
});

app.get('/other_routes', function() {
  //...
});

ตอบโจทย์มาก! ฉันได้เห็นการใช้งานอื่น ๆ ของ next แล้วเช่น next (err) และ next ('route') ตอนนี้คุณมีจุดประสงค์หรือไม่เมื่อคุณต้องการเผยแพร่ข้อผิดพลาดและเมื่อใดที่คุณต้องการข้ามไปยังเส้นทางที่แน่นอน
Andreas Selenwall

8
@AndreasSelenwall next('route')มีไว้สำหรับapp.VERB()และใช้เมื่อเส้นทางมีการเรียกกลับหลายครั้งเพื่อ " ข้ามการเรียกกลับเส้นทางที่เหลือ " next(err)ใช้เพื่อข้ามไปยัง " ตัวกลางที่มีข้อผิดพลาด " ( Eจากโพสต์)
Jonathan Lonowski

5
upvoted สำหรับวลีนี้: "ล้อเล่นจริงๆฉันไม่ต้องการจัดการเรื่องนี้" คุณทำให้ฉันเข้าใจสิ่งที่ฉันกำลังมองหา ฉันเห็นคำถามที่คล้ายกันมากมายและพบวิธีแก้ปัญหาในวลีเดียว
Pedro Barros

8
การโทรnext()โดยไม่มีข้อโต้แย้งไม่ได้เป็นการบอกระบบว่า "ฉันไม่ต้องการจัดการเรื่องนี้" แต่เพียงแค่บอกให้ระบบประมวลผลตัวกลางที่เหลืออยู่ต่อไปหลังจากเสร็จสิ้น ไม่ใช่เงื่อนไขข้อผิดพลาดในการโทรnext()แต่เป็นส่วนหนึ่งของโฟลว์ปกติ หากคุณไม่โทรจะnext()ไม่มีการดำเนินการเส้นทางอื่นเลย
d512

1
ฉันต้องการคำตอบเพิ่มเติมในเว็บไซต์นี้ที่เขียนขึ้นสำหรับคนธรรมดา ... 'next () โดยไม่มีข้อโต้แย้งว่า "ล้อเล่นฉันไม่ต้องการจัดการเรื่องนี้จริงๆ"' - เหมาะสำหรับ noobs ... (@ d512 has a อธิบายได้ดีกว่า)
daCoda

129

ในเฟรมเวิร์กส่วนใหญ่คุณจะได้รับคำขอและต้องการตอบกลับ เนื่องจากลักษณะ async ของ Node.js คุณจะพบปัญหากับการโทรกลับที่ซ้อนกันหากคุณกำลังทำสิ่งที่ไม่สำคัญ เพื่อป้องกันไม่ให้สิ่งนี้เกิดขึ้น Connect.js (ก่อนหน้า v4.0 Express.js เป็นเลเยอร์ที่อยู่ด้านบนของ connect.js) มีสิ่งที่เรียกว่ามิดเดิลแวร์ซึ่งเป็นฟังก์ชันที่มีพารามิเตอร์ 2, 3 หรือ 4

function (<err>, req, res, next) {}

แอป Express.js ของคุณเป็นกลุ่มของฟังก์ชันเหล่านี้

ใส่คำอธิบายภาพที่นี่

เราเตอร์มีความพิเศษเป็นมิดเดิลแวร์ที่ช่วยให้คุณเรียกใช้มิดเดิลแวร์ตั้งแต่หนึ่งรายการขึ้นไปสำหรับ URL หนึ่ง ๆ มันก็คือกองซ้อนในกอง

แล้วต่อไปจะทำอะไร? ง่ายๆคือบอกให้แอปของคุณเรียกใช้มิดเดิลแวร์ถัดไป แต่จะเกิดอะไรขึ้นเมื่อคุณผ่านอะไรต่อไป? Express จะยกเลิกสแต็กปัจจุบันและจะเรียกใช้มิดเดิลแวร์ทั้งหมดที่มีพารามิเตอร์ 4 ตัว

function (err, req, res, next) {}

มิดเดิลแวร์นี้ใช้เพื่อประมวลผลข้อผิดพลาดใด ๆ ฉันชอบทำสิ่งต่อไปนี้:

next({ type: 'database', error: 'datacenter blew up' });

ด้วยข้อผิดพลาดนี้ฉันอาจจะบอกผู้ใช้ว่ามีบางอย่างผิดพลาดและบันทึกข้อผิดพลาดจริง

function (err, req, res, next) {
   if (err.type === 'database') {
     res.send('Something went wrong user');
     console.log(err.error);
   }
};

หากคุณวาดภาพแอปพลิเคชัน Express.js ของคุณเป็นสแต็กคุณอาจสามารถแก้ไขความแปลกประหลาดได้มากมาย ตัวอย่างเช่นเมื่อคุณเพิ่มมิดเดิลแวร์คุกกี้ของคุณหลังจากที่คุณเราเตอร์มันสมเหตุสมผลแล้วที่เส้นทางของคุณจะไม่มีคุกกี้


3
คุณจะเก็บฟังก์ชันการบันทึกแบบไม่ระบุตัวตนนี้ไว้ที่ใด
dennismonsewicz

"Express จะยกเลิกสแต็กปัจจุบันและจะเรียกใช้มิดเดิลแวร์ทั้งหมดที่มีพารามิเตอร์ 4 ตัว" ฉันสงสัยว่า Express สามารถเรียกใช้ตัวกลางตัวจัดการข้อผิดพลาดที่แน่นอนได้อย่างไร ขอบคุณ!
Abhishek Agarwal

75

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

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

app.get('/user/:id', function (req, res, next) {
    console.log('before request handler');
    next();
});

app.get('/user/:id', function (req, res, next) {
    console.log('handling request');
    res.sendStatus(200);
    next();
});

app.get('/user/:id', function (req, res, next) {
    console.log('after request handler');
    next();
});

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

ถ้าคุณทำ

curl http://localhost:3000/user/123

คุณจะเห็นสิ่งนี้พิมพ์ลงในคอนโซล:

before request handler
handling request
after request handler

ตอนนี้ถ้าคุณแสดงความคิดเห็นในการโทรไปยังnext()ตัวจัดการกลางเช่นนี้:

app.get('/user/:id', function (req, res, next) {
    console.log('handling request');
    res.sendStatus(200);
    //next();
});

คุณจะเห็นสิ่งนี้บนคอนโซล:

before request handler
handling request

โปรดสังเกตว่าตัวจัดการสุดท้าย (ตัวที่พิมพ์after request handler) ไม่ทำงาน นั่นเป็นเพราะคุณไม่ได้บอกให้ Express เรียกใช้ตัวจัดการคนถัดไปอีกต่อไป

ดังนั้นจึงไม่ได้เรื่องจริงๆถ้าคุณ "หลัก" จัดการ (หนึ่งที่ผลตอบแทน 200) ประสบความสำเร็จหรือไม่ถ้าคุณต้องการที่เหลือของ middlewares next()ในการทำงานคุณจะต้องโทร

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

app.get('/user/:id', function (req, res, next) {
    try {
       // ...
    }
    catch (ex) {
       // ...
    }
    finally {
       // go to the next handler regardless of what happened in this one
       next();
    }
});

app.get('/user/:id', function (req, res, next) {
    logToDatabase(req);
    next();
});

หากคุณต้องการให้ตัวจัดการมือสองทำงานคุณต้องโทรหาnext()ตัวจัดการแรก

โปรดจำไว้ว่าโหนดเป็นแบบ async ดังนั้นจึงไม่สามารถทราบได้ว่าเมื่อใดที่การเรียกกลับของตัวจัดการคนแรกเสร็จสิ้น next()คุณต้องบอกว่าโดยการเรียก


Sonfar เป็นคำตอบที่ดีที่สุดสำหรับฉัน
Norayr Ghukasyan

คำอธิบายดี!
Chang

7

next () โดยไม่มีพารามิเตอร์เรียกใช้ตัวจัดการเส้นทางถัดไปหรือมิดเดิลแวร์ถัดไปในเฟรมเวิร์ก


2
หรือตัวกลางต่อไป.
robertklep


1

สังเกตการเรียกด้านบนเพื่อถัดไป () การเรียกใช้ฟังก์ชันนี้จะเรียกใช้ฟังก์ชันมิดเดิลแวร์ถัดไปในแอป ฟังก์ชัน next () ไม่ใช่ส่วนหนึ่งของ Node.js หรือ Express API แต่เป็นอาร์กิวเมนต์ที่สามที่ส่งผ่านไปยังฟังก์ชันมิดเดิลแวร์ ฟังก์ชัน next () สามารถตั้งชื่ออะไรก็ได้ แต่ตามรูปแบบแล้วจะมีชื่อว่า "next" เสมอ เพื่อหลีกเลี่ยงความสับสนให้ใช้หลักการนี้เสมอ


0

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

  1. การใช้งานถัดไป ():

ในระยะสั้น: ฟังก์ชันมิดเดิลแวร์ถัดไป

ดึงข้อมูลจากเอกสาร Express JSอย่างเป็นทางการ- หน้า 'writing-middleware' :

"myLogger ฟังก์ชันมิดเดิลแวร์เพียงพิมพ์ข้อความจากนั้นส่งคำขอไปยังฟังก์ชันมิดเดิลแวร์ถัดไปในสแตกโดยเรียกฟังก์ชัน next ()"

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

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

app.use(myLogger)

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

app.listen(3000)

หน้าเอกสาร Express JS นี้ระบุว่า "หากฟังก์ชันมิดเดิลแวร์ปัจจุบันไม่สิ้นสุดรอบการตอบสนองคำขอจะต้องเรียกใช้ next () เพื่อส่งผ่านการควบคุมไปยังฟังก์ชันมิดเดิลแวร์ถัดไปมิฉะนั้นคำขอจะค้างอยู่"

  1. การใช้งานถัดไป ('เส้นทาง'):

กล่าวโดยย่อ: เส้นทางถัดไป (เทียบกับฟังก์ชันมิดเดิลแวร์ถัดไปในกรณีถัดไป ())

แยกจากเอกสาร Express JS - หน้า 'using-middleware' :

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

ตัวอย่างนี้แสดงสแต็กย่อยมิดเดิลแวร์ที่จัดการคำขอ GET ไปยังพา ธ / user /: id "

app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id === '0') next('route')
  // otherwise pass the control to the next middleware function in this stack
  else next()
}, function (req, res, next) {
  // render a regular page
  res.render('regular')
})

// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
  res.render('special')
})

0

next ()คืออาร์กิวเมนต์เรียกกลับไปยังฟังก์ชันมิดเดิลแวร์ที่มี req และ res เป็นคำขอ http และอาร์กิวเมนต์ตอบกลับถัดไปในโค้ดด้านล่าง

app.get ('/', (req, res, ถัดไป) => {next ()});

ถัดไป () เรียกใช้ฟังก์ชันที่ส่งผ่านในมิดเดิลแวร์ หากฟังก์ชันมิดเดิลแวร์ปัจจุบันไม่สิ้นสุดรอบการตอบสนองการร้องขอควรเรียก next () มิฉะนั้นคำขอจะค้างและจะหมดเวลา

next () fn ต้องถูกเรียกภายในฟังก์ชันมิดเดิลแวร์แต่ละฟังก์ชันเมื่อฟังก์ชันมิดเดิลแวร์หลายฟังก์ชันถูกส่งผ่านไปยังapp.useหรือ app.METHOD มิฉะนั้นฟังก์ชันมิดเดิลแวร์ถัดไปจะไม่ถูกเรียก (ในกรณีที่มีการส่งผ่านฟังก์ชันมิดเดิลแวร์มากกว่า 1 ฟังก์ชัน) หากต้องการข้ามการเรียกฟังก์ชันมิดเดิลแวร์ที่เหลือให้เรียกnext ('route')ภายในฟังก์ชันมิดเดิลแวร์หลังจากนั้นไม่ควรเรียกใช้ฟังก์ชันมิดเดิลแวร์อื่น ๆ ในโค้ดด้านล่าง fn1 จะถูกเรียกและ fn2 จะถูกเรียกด้วยเนื่องจาก next () ถูกเรียกภายใน fn1 อย่างไรก็ตามจะไม่มีการเรียก fn3 เนื่องจากมีการเรียก ('route') ถัดไปภายใน fn2

app.get('/fetch', function fn1(req, res, next)  {
console.log("First middleware function called"); 
    next();
}, 
function fn2(req, res, next) {
    console.log("Second middleware function called"); 
    next("route");
}, 
function fn3(req, res, next) {
    console.log("Third middleware function will not be called"); 
    next();
})
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.