พารามิเตอร์“ ถัดไป” ที่ใช้ใน Express คืออะไร


295

สมมติว่าคุณมีรหัสง่ายๆแบบนี้:

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

ฟังก์ชั่นนี้มีสองพารามิเตอร์reqและresซึ่งเป็นตัวแทนของวัตถุร้องขอและตอบสนองตามลำดับ

บนมืออื่น ๆ ที่มีฟังก์ชั่นอื่น ๆ nextที่มีพารามิเตอร์ที่สามเรียกว่า ตัวอย่างเช่นลองมาดูรหัสต่อไปนี้:

app.get('/users/:id?', function(req, res, next){ // Why do we need next?
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); // What is this doing?
    }
});

ฉันไม่เข้าใจว่าประเด็นnext()คืออะไรหรือเพราะเหตุใดจึงใช้งาน ในตัวอย่างนั้นถ้าไม่มี id สิ่งที่กำลังnextทำอยู่จริง


13
ถัดไปอนุญาตให้ตัวจัดการเส้นทางถัดไปอยู่ในบรรทัดเพื่อจัดการการร้องขอ ในกรณีนี้หากมี ID ผู้ใช้ก็น่าจะใช้res.sendในการดำเนินการตามคำขอ หากไม่มีอยู่อาจมีตัวจัดการอื่นที่จะออกข้อผิดพลาดและดำเนินการตามคำขอให้เสร็จสมบูรณ์
Dominic Barnes

1
ดังนั้นคุณจะบอกว่าฉันมีapp.post('/login',function(req,res))หลังจากapp.get('/users',function(req,res)) นั้นจะเรียกเข้าสู่ระบบเป็นเส้นทางต่อไปในไฟล์ app.js โดยการเรียกต่อไป ()
Menztrual

2
ไม่คุณควรอ้างถึงส่วนนี้ของเอกสาร Express.js: expressjs.com/guide.html#passing-route control
Dominic Barnes

3
โดยทั่วไปเส้นทางถัดไปที่จะเรียกใช้จะเป็นอีกเส้นทางหนึ่งที่ URL สำหรับคำขอตรงกัน ในกรณีนี้หากมีการลงทะเบียนเส้นทางอื่นผ่านเส้นทางapp.get("/users")นั้นจะถูกเรียกใช้หากตัวจัดการเหนือการโทรถัดไป
Dominic Barnes

3
ถัดไปเป็นเพียงแค่การโทรกลับ
Jonathan Ong

คำตอบ:


266

มันผ่านการควบคุมไปยังเส้นทางการจับคู่ต่อไป ในตัวอย่างที่คุณให้ตัวอย่างเช่นคุณอาจมองถึงผู้ใช้ในฐานข้อมูลหากผู้ได้รับพระราชทานและกำหนดให้idreq.user

ด้านล่างคุณอาจมีเส้นทางเช่น:

app.get('/users', function(req, res) {
  // check for and maybe do something with req.user
});

เนื่องจาก / users / 123 จะจับคู่เส้นทางในตัวอย่างของคุณก่อนซึ่งจะทำการตรวจสอบและค้นหาผู้ใช้123ก่อน จากนั้น/usersสามารถทำอะไรกับผลลัพธ์ของสิ่งนั้น

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

function loadUser(req, res, next) {
  if (req.params.userId) {
    Users.findOne({ id: req.params.userId }, function(err, user) {
      if (err) {
        next(new Error("Couldn't find user: " + err));
        return;
      }

      req.user = user;
      next();
    });
  } else {
    next();
  }
}

// ...

app.get('/user/:userId', loadUser, function(req, res) {
  // do something with req.user
});

app.get('/users/:userId?', loadUser, function(req, res) {
  // if req.user was set, it's because userId was specified (and we found the user).
});

// Pretend there's a "loadItem()" which operates similarly, but with itemId.
app.get('/item/:itemId/addTo/:userId', loadItem, loadUser, function(req, res) {
  req.user.items.append(req.item.name);
});

ความสามารถในการควบคุมโฟลว์เช่นนี้ค่อนข้างมีประโยชน์ คุณอาจต้องการให้ผู้ใช้ที่มีสถานะผู้ดูแลระบบเท่านั้น:

/**
 * Only allows the page to be accessed if the user is an admin.
 * Requires use of `loadUser` middleware.
 */
function requireAdmin(req, res, next) {
  if (!req.user || !req.user.admin) {
    next(new Error("Permission denied."));
    return;
  }

  next();
}

app.get('/top/secret', loadUser, requireAdmin, function(req, res) {
  res.send('blahblahblah');
});

หวังว่านี่จะเป็นแรงบันดาลใจให้คุณ!


คุณสามารถพูดได้! ฉันมีแนวโน้มที่จะทำสิ่งนี้กับมิดเดิลแวร์ของเส้นทางมากกว่าเนื่องจากไม่ได้จับคู่ตรรกะกับลำดับเส้นทางเฉพาะหรือโครงสร้าง URI เฉพาะ
Asherah

5
ทำไมบางครั้งคุณกลับมาครั้งต่อไป () แต่บางครั้งก็ทำไม่ได้
John

6
@John: ค่าส่งคืนถูกละเว้นจริง ฉันแค่อยากกลับไปที่นั่นเพื่อให้แน่ใจว่าฉันจะไม่โทรnext()อีกครั้ง next(new Error(…)); return;มันจะเป็นเหมือนกันถ้าฉันเพียงแค่ใช้
Asherah

1
@ level0: ค่าส่งคืนจะถูกละเว้น next(new Error(…)); return;คุณสามารถพิจารณามันชวเลข ถ้าเราส่งค่าไปnext, ก็ถือว่ามีข้อผิดพลาดเพียงฝ่ายเดียว ฉันไม่ได้มองเป็นรหัสด่วนมากเกินไป แต่ขุดรอบและคุณจะพบสิ่งที่คุณต้องการ :)
เสารูปเคารพ

1
@ level0: (ฉันได้เปลี่ยนreturn next(…);เป็นnext(…); return;ดังนั้นมันจึงสับสนน้อยกว่า)
Asherah

87

ฉันยังมีปัญหาในการทำความเข้าใจต่อไป () แต่สิ่งนี้ช่วย

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

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write("Hello");
    next(); //remove this and see what happens 
});

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write(" World !!!");
    httpResponse.end();
});

app.listen(8080);

3
รวบรัดมาก! ขอบคุณ! แต่คุณจะแน่ใจได้อย่างไรว่าคนแรก.getถูกเรียกและไม่ใช่คนที่ 2?
JohnnyQ

18
@JohnnyQ มันจะถูกดำเนินการจากบนลงล่าง
Tapash

59

ก่อนที่จะเข้าใจnextคุณจำเป็นต้องมีความคิดเล็กน้อยเกี่ยวกับวงจรการตอบสนองคำขอในโหนดแม้ว่าจะมีรายละเอียดไม่มากนัก มันเริ่มต้นด้วยการที่คุณทำการร้องขอ HTTP สำหรับทรัพยากรเฉพาะและจะสิ้นสุดลงเมื่อคุณส่งการตอบกลับกลับไปยังผู้ใช้เช่นเมื่อคุณพบสิ่งที่ต้องการ res.send ('Hello World');

ลองมาดูตัวอย่างที่ง่ายมาก

app.get('/hello', function (req, res, next) {
  res.send('USER')
})

ที่นี่เราไม่จำเป็นต้องต่อไป () เพราะ resp.send จะสิ้นสุดรอบและมอบการควบคุมกลับไปที่มิดเดิลแวร์ของเส้นทาง

ตอนนี้ลองมาดูตัวอย่างอื่น

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

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

ที่นี่เรามีฟังก์ชั่นมิดเดิลแวร์ 2 รายการสำหรับเส้นทางเดียวกัน แต่คุณจะได้รับคำตอบจากคนแรกเสมอ เพราะที่ติดตั้งครั้งแรกในมิดเดิลแวร์สแต็คและ res.send จะสิ้นสุดรอบ

แต่ถ้าเราไม่ต้องการ“ Hello World !!!!” ตอบกลับ สำหรับเงื่อนไขบางอย่างเราอาจต้องการ "Hello Planet !!!!" คำตอบ ลองแก้ไขโค้ดด้านบนและดูว่าเกิดอะไรขึ้น

app.get('/hello', function (req, res, next) {
  if(some condition){
    next();
    return;
  }
  res.send("Hello World !!!!");  
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

สิ่งที่nextทำอะไรที่นี่ และใช่คุณอาจมี gusses มันจะข้ามฟังก์ชันมิดเดิลแวร์แรกหากเงื่อนไขเป็นจริงและเรียกใช้ฟังก์ชันมิดเดิลแวร์ถัดไปและคุณจะได้รับการ"Hello Planet !!!!"ตอบกลับ

ดังนั้นถัดไปผ่านการควบคุมไปยังฟังก์ชั่นถัดไปในกองกลางมิดเดิลแวร์

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

สิ่งที่ต้องการด้านล่าง: -

app.get('/hello', function (req, res, next) {
  // Your piece of logic
  next();
});

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

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

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


ดังนั้นnext()ทำงานเหมือนgotoป้ายกำกับแบบมีสายแข็งหรือไม่ นั่นคือในตัวอย่างที่สามของคุณเมื่อคุณโทรnext(), res.send("Hello World !!!!"); จะไม่ถูกดำเนินการ? ฉันสังเกตเห็นว่า @Ashe มักจะมีสายreturn;หลังจากnextที่มีรหัสในต้นไม้การดำเนินการเดียวกัน ... เดาว่าฉันสามารถเช็คอินด่วนเสมอใช่มั้ย? / วิ่งไปที่โปรแกรมแก้ไขข้อความของเขา;)
ruffin

@ รัฟฟินใช่คุณสามารถคิดต่อไปคล้ายกับข้ามไป แต่ถัดไปรู้ว่าจะต้องไปที่ไหนเหมือนข้ามไปซึ่งต้องใช้ฉลาก ถัดไปจะผ่านการควบคุมไปยังฟังก์ชั่นมิดเดิลแวร์ต่อไป นอกจากนี้คุณยังสามารถตั้งชื่ออะไรก็ได้ 'next' มันเป็นเพียงฉลากที่นี่ แต่วิธีปฏิบัติที่ดีที่สุดคือใช้ชื่อ 'ถัดไป'
Mav55

3
ตกลงดูเหมือนว่าไม่ถูกต้อง ฉันพยายามรหัส ( Pastebin ที่นี่ ) และรหัสหลังจากที่next()โทรเรียกว่า ในกรณีpast the next() callนี้เขียนไปยังคอนโซลแล้วฉันได้รับError: Can't set headers after they are sent.ข้อผิดพลาดตามที่สองres.sendเรียกว่าแม้ว่าจะไม่สำเร็จ การไหลของรหัสจะกลับมาหลังจากการnext()โทรซึ่งทำให้ @ Ashe's returns(หรือการจัดการตรรกะอื่น ๆ ) มีความสำคัญ
ruffin

4
@ รัฟฟินใช่คุณพูดถูก เราต้องการคำสั่งคืนสินค้าหลังจากที่next()เพื่อข้ามการดำเนินการของคำสั่งที่เหลือ ขอบคุณสำหรับการชี้ให้เห็นว่า
Mav55

1
ขอขอบคุณที่อธิบายว่า "มิดเดิลแวร์" คืออะไร / ทำตัวอย่างที่ชัดเจนไม่ได้เป็นเพียงการทำให้เอกสารเป็นเรื่องง่าย นี่เป็นคำตอบเดียวที่จริง ๆ แล้วพูดอะไรที่ชัดเจนเกี่ยวกับสิ่งที่เกิดขึ้นทำไม & อย่างไร
mc01

11

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


6
ฉันไม่แน่ใจว่าคำตอบนี้จะเพิ่มคำถามอายุเจ็ดขวบ ...
mherzig

แม้ว่าโดยย่อความคิดเห็นนี้ทำให้ฉันเป็นอย่างนี้: expressjs.com/th/guide/writing-middleware.html
hmak

@herzig มันเป็นหนึ่งซับและครอบคลุมทุกอย่าง
เอ็มโกปาล

5

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


2

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

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