เมื่อใดที่จะใช้ next () และ return next () ใน Node.js


136

สถานการณ์จำลอง : พิจารณาสิ่งต่อไปนี้เป็นส่วนหนึ่งของรหัสจากโหนดเว็บแอป

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); //or return next();
    }
});

ฉบับที่ : ฉันกำลังตรวจสอบที่หนึ่งที่จะไปมีเพียงหรือnext() return next()โค้ดตัวอย่างด้านบนทำงานได้เหมือนกันทั้งสอง & ไม่แสดงความแตกต่างในการเรียกใช้

คำถาม : ใครบางคนสามารถให้ความกระจ่างเกี่ยวกับเรื่องนี้เมื่อใดควรใช้next()และเมื่อใดreturn next()และมีความแตกต่างที่สำคัญหรือไม่?

คำตอบ:


141

บางคนมักจะเขียนreturn next()เพื่อให้แน่ใจว่าการดำเนินการหยุดหลังจากเรียกโทรกลับ

หากคุณไม่ทำเช่นนั้นคุณอาจเสี่ยงต่อการโทรกลับมาอีกครั้งในภายหลังซึ่งมักจะมีผลเสียหาย รหัสของคุณใช้ได้ดี แต่ฉันจะเขียนใหม่เป็น:

app.get('/users/:id?', function(req, res, next){
    var id = req.params.id;

    if(!id)
        return next();

    // do something
});

มันช่วยฉันในระดับการย่อหน้าและเมื่อฉันอ่านรหัสอีกครั้งในภายหลังฉันแน่ใจว่าไม่มีทางที่nextจะถูกเรียกสองครั้ง


2
สิ่งที่คล้ายกันจะเป็นจริงres.redirect('/')กับเมื่อเทียบกับreturn res.redirect('/')ในสถานการณ์ประเภทนี้? อาจจะเป็นการดีกว่าถ้าคุณเขียนการส่งคืนคำสั่ง res ด้านหน้าเสมอเพื่อหลีกเลี่ยงข้อผิดพลาดของการตั้งค่าส่วนหัวหลังจากส่งไป
อดัมดี

185

ตามคำตอบของ @Laurent Perrin:

หากคุณไม่ทำเช่นนั้นคุณอาจเสี่ยงต่อการโทรกลับมาอีกครั้งในภายหลังซึ่งมักจะมีผลเสียหาย

ฉันให้ตัวอย่างที่นี่ถ้าคุณเขียนมิดเดิลแวร์แบบนี้:

app.use((req, res, next) => {
  console.log('This is a middleware')
  next()
  console.log('This is first-half middleware')
})

app.use((req, res, next) => {
  console.log('This is second middleware')
  next()
})

app.use((req, res, next) => {
  console.log('This is third middleware')
  next()
})

คุณจะพบว่าผลลัพธ์ในคอนโซลคือ:

This is a middleware
This is second middleware
This is third middleware
This is first-half middleware

นั่นคือมันรันโค้ดด้านล่างถัดไป () หลังจากฟังก์ชั่นมิดเดิลแวร์ทั้งหมดเสร็จสิ้น

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


29
ในฐานะที่เป็นผู้เริ่มต้นของexpressคำตอบนี้ทำให้ฉันชัดเจนกว่าคำตอบอื่น ๆ ยกนิ้ว!
แมนดาริน

1
สิ่งที่คล้ายกันจะเป็นจริงres.redirect('/')กับเมื่อเทียบกับreturn res.redirect('/')ในสถานการณ์ประเภทนี้? อาจจะเป็นการดีกว่าถ้าคุณเขียนข้อความreturnต่อหน้าเสมอresเพื่อหลีกเลี่ยงข้อผิดพลาดในการตั้งค่าส่วนหัวหลังจากส่งไป
อดัมดี

1
ทำไมฉันต้องเขียนโค้ดหลังจากถัดไป () เห็นได้ชัดหรือไม่ว่าฉันไม่ทำอะไรหลังจากเสร็จงานในมิดเดิลแวร์? @PJCHENder
Imran Pollob

1
@ImranPollob เกิดข้อผิดพลาดบางครั้งเกิดขึ้น เมื่อคุณเขียนโค้ดจำนวนมาก ifs / elses / etc คุณอาจลืม `` `return next ()`
Jone Polvora

46

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

ในกรณีที่คุณต้องการหยุดการไหลของฟังก์ชั่นที่คุณสามารถใช้next(err)ดังต่อไปนี้

app.get('/user/:id?', 
    function(req, res, next) { 
        console.log('function one');
        if ( !req.params.id ) 
            next('No ID'); // This will return error
        else   
            next(); // This will continue to function 2
    },
    function(req, res) { 
        console.log('function two'); 
    }
);

ค่อนข้างมากnext()ใช้สำหรับการขยายมิดเดิลแวร์ของคำขอของคุณ


1
เราสามารถส่งพารามิเตอร์ที่ชอบ: next('No ID')?
Amol M Kulkarni

7
next('No ID')กำลังส่งข้อผิดพลาดซึ่งจะทำให้การไหลเวียนผิดพลาด
drinchev

ใช้ถัดไป (null, "somevalue"); สำหรับเครื่องมืออย่าง async.waterfall มันจะส่งค่าไปยังฟังก์ชันถัดไป สำหรับชุดการโต้ตอบที่ซับซ้อนซึ่งขับเคลื่อนด้วยข้อมูลฉันมักจะผ่านวัตถุบริบทระหว่างฟังก์ชั่น ด้วยวิธีนี้ฉันสามารถสร้างฟังก์ชั่นทั่วไปที่สามารถแชร์ข้ามจุดปลายหลายจุดและควบคุมการไหลผ่านข้อมูลในบริบท
Chad Wilson

5
"ดังนั้นให้ส่งคืน next () และ next (); return; โดยพื้นฐานแล้วจะเหมือนกัน" - สิ่งที่ฉันต้องการอ่าน ขอบคุณ @drinchev
Nick Pineda

1
ฉันสังเกตสิ่งที่ตรงกันข้าม (เมื่อเกิดข้อผิดพลาดในการยิง): ถัดไป (ข้อผิดพลาด) ทริกเกอร์มิดเดิลแวร์ต่อไป แต่ยังคงรันโค้ด; คืนค่า next (ข้อผิดพลาด) เพียงแค่ลดการประมวลผลไปยังมิดเดิลแวร์หน้า next (e) และ return ถัดไป (e) ไม่เหมือนกัน
Nickolodeon

0

เป็นการดีที่สุดที่จะไม่ใช้เลย! ฉันอธิบายและนั่นคือสิ่งที่ฉันอธิบายด้วย

ฟังก์ชันถัดไป () ที่สามารถมีชื่อใด ๆ และตามระเบียบได้รับการตั้งค่าเป็นถัดไป มันเกี่ยวข้องกับการดำเนินการทางอ้อม (PUT, GET, DELETE, ... ) ซึ่งโดยทั่วไปแล้วจะดำเนินการบนทรัพยากร URI เดียวกันเช่น/ user /: id

app.get('/user/:id', function (req,res,next)...)
app.put('/user/:id', function (req,res,next)...)
app.delete('/user/:id', function (req,res,next)...)
app.post('/user/', function ()...)

ตอนนี้ถ้าคุณดูที่ app.get, app.put และ app.delete ให้ใช้ uri (/ user /: id) เดียวกันสิ่งเดียวที่แยกความแตกต่างได้คือการนำไปใช้ เมื่อมีการร้องขอ (req) express จะทำการใส่ req เป็นอันดับแรกใน app.get หากการตรวจสอบใด ๆ ที่คุณสร้างขึ้นเนื่องจากคำขอนั้นไม่ได้สำหรับตัวควบคุมนั้นจะล้มเหลวก็จะส่งผ่าน req ไปยัง app.put ซึ่งเป็นเส้นทางถัดไปในไฟล์ te บน. ดังที่เห็นในตัวอย่างด้านล่าง

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

    if(req.method === 'GET')
    //whatever you are going to do
    else
      return next() //it passes the request to app.put

    //Where would GET response 404 go, here? or in the next one. 
    // Will the GET answer be handled by a PUT? Something is wrong here.

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

    if(req.method === 'PUT')
    //whatever you are going to do
    else
      return next()

   })

ปัญหาอยู่ที่ว่าในท้ายที่สุดคุณจะผ่านการร้องขอไปยังตัวควบคุมทั้งหมดโดยหวังว่าจะมีสิ่งที่คุณต้องการผ่านการตรวจสอบความถูกต้องของ req ในที่สุดผู้ควบคุมทั้งหมดก็จะได้รับสิ่งที่ไม่ใช่สำหรับพวกเขา :(

ดังนั้นวิธีการหลีกเลี่ยงปัญหาต่อไป () ?

คำตอบนั้นง่ายจริงๆ.

1- ควรมีเพียงหนึ่ง uriเพื่อระบุทรัพยากร

http: // IpServidor / colection /: resource / colection /: resourceหาก URI ของคุณยาวกว่านั้นคุณควรพิจารณาสร้าง uri ใหม่

ตัวอย่างhttp: // IpServidor / users / pepe / contacts / contacto1

2-All การดำเนินการเกี่ยวกับทรัพยากรนี้จะต้องดำเนินการเคารพ idempotence ของคำกริยา http (รับ, โพสต์, วาง, ลบ, ... ) ดังนั้นการโทรไปยัง URI มีวิธีการโทรเพียงวิธีเดียวเท่านั้น

POST http://IpServidor/users/  //create a pepe user 
GET http://IpServidor/users/pepe  //user pepe returns   
PUT http://IpServidor/users/pepe  //update the user pepe 
DELETE http://IpServidor/users/pepe  //remove the user pepe

ข้อมูลเพิ่มเติม [ https://docs.microsoft.com/es-es/azure/architecture/best-practices/api-design#organize-the-api-around-resources เหมือนพี่น้อง 1]

ลองดูรหัส! การใช้งานอย่างเป็นรูปธรรมที่ทำให้เราหลีกเลี่ยงการใช้งานถัดไป ()!

ในไฟล์ index.js

//index.js the entry point to the application also caller app.js
const express = require('express');
const app = express();

const usersRoute = require('./src/route/usersRoute.js');

app.use('/users', usersRoute );

ในไฟล์ usersRoute.js

    //usersRoute.js
    const express = require('express');
    const router = express.Router();

    const getUsersController = require('../Controllers/getUsersController.js');
    const deleteUsersController = require('../Controllers/deleteUsersController.js');

    router.use('/:name', function (req, res) //The path is in /users/:name
    {
    switch (req.method)
    {
    case 'DELETE':
      deleteUsersController(req, res);
      break;
    case 'PUT':
     // call to putUsersController(req, res);
     break;
    case 'GET':
     getUsersController(req, res);
     break;
    default:
     res.status(400).send('Bad request');
    } });

router.post('/',function (req,res) //The path is in /users/
{
    postUsersController(req, res);
});

module.exports = router;

ตอนนี้ไฟล์ usersRoute.js ทำในสิ่งที่ไฟล์ที่เรียกว่า usersRoute ซึ่งคาดว่าจะทำซึ่งก็คือการจัดการเส้นทางของ URI / users /

// ไฟล์ getUsersController.js

//getUsersController.js
    const findUser= require('../Aplication/findUser.js');
    const usersRepository = require('../Infraestructure/usersRepository.js');

    const getUsersController = async function (req, res)
    {

       try{
          const userName = req.params.name;
        //...
          res.status(200).send(user.propertys())

        }catch(findUserError){
           res.status(findUserError.code).send(findUserError.message)
        }
    }
   module.exports = getUsersController;

ด้วยวิธีนี้คุณหลีกเลี่ยงการใช้งานในครั้งต่อไปคุณแยกรหัสคุณได้รับประสิทธิภาพคุณพัฒนา SOLID คุณเปิดประตูทิ้งไว้สำหรับการโยกย้ายที่เป็นไปได้เพื่อ microservices และเหนือสิ่งอื่นใดมันง่ายต่อการอ่านโดยโปรแกรมเมอร์


2
สิ่งนี้ไม่ถูกต้อง app.get จะไม่ส่งผ่านไปยัง app.put ตามที่คุณแนะนำ คำขอที่ตรงกันเท่านั้นที่ถูกเรียกใช้ดังนั้นหากเมธอดเป็น GET เฉพาะมิดเดิลแวร์ของ app.get เท่านั้นที่จะถูกเรียก มิดเดิลแวร์ไม่จำเป็นต้องตรวจสอบวิธีการร้องขอ ข้อเสนอแนะของคุณจะไม่สนใจฟังก์ชันหลักของ express และใช้การกำหนดเส้นทางของคุณเองแทน นอกจากนี้ข้อเสนอแนะของคุณจะถือว่าเส้นทางของคุณเป็นมิดเดิลแวร์เดียวที่คุณจะใช้เนื่องจากไม่เคยผ่านที่ใดก็ได้
Ravenex

ข้อมูลไม่ถูกต้องโปรดดูคำตอบข้างต้น
DDiamond

-3

ต่อไป() :

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

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