Khi nào nên sử dụng next () và quay lại next () trong Node.js


136

Kịch bản : Xem xét phần sau đây là một phần mã từ ứng dụng web nút.

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

Vấn đề : Tôi đang kiểm tra cái nào sẽ đi với chỉ next()hoặc return next(). Mã mẫu trên hoạt động chính xác như nhau cho cả hai & không cho thấy bất kỳ sự khác biệt nào trong thực thi.

Câu hỏi : Ai đó có thể đưa ra ánh sáng về điều này, khi nào nên sử dụng next()và khi nào sử dụng return next()và một số khác biệt quan trọng?

Câu trả lời:


141

Một số người luôn viết return next()là để đảm bảo rằng việc thực thi dừng lại sau khi kích hoạt cuộc gọi lại.

Nếu bạn không làm điều đó, bạn có nguy cơ kích hoạt cuộc gọi lại lần thứ hai sau đó, điều này thường có kết quả tàn phá. Mã của bạn vẫn ổn, nhưng tôi sẽ viết lại thành:

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

    if(!id)
        return next();

    // do something
});

Nó tiết kiệm cho tôi một mức thụt đầu dòng và khi tôi đọc lại mã sau đó, tôi chắc chắn không có cách nào nextđược gọi hai lần.


2
Điều tương tự sẽ đúng res.redirect('/')với so với return res.redirect('/')loại tình huống này? Có lẽ tốt hơn là luôn luôn viết trở lại trước các câu lệnh res để tránh các lỗi thiết lập tiêu đề sau khi chúng được gửi?
Adam D

185

Như câu trả lời của @Laurent Perrin:

Nếu bạn không làm điều đó, bạn có nguy cơ kích hoạt cuộc gọi lại lần thứ hai sau đó, điều này thường có kết quả tàn phá

Tôi đưa ra một ví dụ ở đây nếu bạn viết phần mềm trung gian như thế này:

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()
})

Bạn sẽ thấy rằng đầu ra trong giao diện điều khiển là:

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

Đó là, nó chạy mã bên dưới next () sau khi tất cả chức năng phần mềm trung gian kết thúc.

Tuy nhiên, nếu bạn sử dụng return next(), nó sẽ nhảy ra gọi lại ngay lập tức và mã bên dưới return next()trong cuộc gọi lại sẽ không thể truy cập được.


29
Là người mới bắt đầu expresscâu trả lời này làm cho mọi thứ rõ ràng hơn với tôi so với các câu trả lời khác. Đồng ý
quan

1
Điều tương tự sẽ đúng res.redirect('/')với so với return res.redirect('/')loại tình huống này? Có lẽ tốt hơn là luôn luôn viết returntrước các rescâu lệnh để tránh các lỗi thiết lập tiêu đề sau khi chúng được gửi?
Adam D

1
Tại sao tôi nên viết mã sau next ()? Không rõ ràng là tôi không làm gì sau khi tôi hoàn thành nhiệm vụ của mình trong một phần mềm trung gian? @PJCHENder
Imran Pollob

1
@ImranPollob đôi khi xảy ra lỗi. Khi bạn viết rất nhiều mã, ifs / elses / vv. Bạn có thể quên `` `return next ()`
Jone Polvora

46

next()là một phần của kết nối trung gian . Các cuộc gọi lại cho luồng bộ định tuyến không quan tâm nếu bạn trả lại bất cứ thứ gì từ các chức năng của mình, vì vậy return next()next(); return;về cơ bản là giống nhau.

Trong trường hợp bạn muốn dừng dòng chức năng, bạn có thể sử dụng next(err)như sau

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'); 
    }
);

Khá nhiều next()được sử dụng để mở rộng phần mềm trung gian của các yêu cầu của bạn.


1
Chúng tôi có thể gửi tham số như : next('No ID')?
Amol M Kulkarni

7
next('No ID')thực sự đang gửi một lỗi, sẽ phá vỡ dòng chảy.
drinchev

Sử dụng tiếp theo (null, "somevalue"); Đối với các công cụ như async.waterfall, nó sẽ chuyển giá trị cho hàm tiếp theo. Đối với một loạt các tương tác phức tạp được điều khiển dữ liệu, tôi thường chuyển một đối tượng ngữ cảnh giữa các hàm. Bằng cách đó, tôi có thể tạo các hàm chung có thể được chia sẻ trên nhiều điểm cuối và kiểm soát luồng qua dữ liệu trong ngữ cảnh
Chad Wilson

5
"vì vậy return return () và next (); return; về cơ bản là giống nhau." - chỉ những gì tôi cần đọc. thx @drinchev
Nick Pineda

1
Tôi quan sát ngược lại (khi bắn lỗi): next (error) kích hoạt phần mềm trung gian tiếp theo, nhưng tiếp tục thực thi mã; return next (error) chỉ cần thực hiện lại phần mềm trung gian tiếp theo. next (e) và return next (e) KHÔNG giống nhau.
Nickolodeon

0

Tốt nhất là không nên sử dụng nó! Tôi giải thích, và đó là những gì tôi làm cũng giải thích nó.

Hàm next () có thể có bất kỳ tên nào và theo quy ước đã được đặt thành next. Nó liên quan gián tiếp đến các hoạt động (PUT, GET, DELETE, ...) thường được thực hiện trên cùng một tài nguyên 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 ()...)

Bây giờ nếu bạn xem app.get, app.put và app.delete sử dụng cùng một uri (/ user /: id), điều duy nhất phân biệt chúng là việc thực hiện chúng. Khi yêu cầu được thực hiện (req) express đặt req lên đầu tiên trong app.get, nếu bất kỳ xác thực nào bạn tạo vì yêu cầu đó không dành cho bộ điều khiển đó không thành công, nó sẽ chuyển req tới app.put là tuyến tiếp theo trong tệp te và vì vậy trên. Như đã thấy trong ví dụ dưới đây.

    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()

   })

Vấn đề nằm ở chỗ, cuối cùng bạn sẽ chuyển req cho tất cả các bộ điều khiển với hy vọng rằng có một cái thực hiện những gì bạn muốn, thông qua việc xác nhận req. Cuối cùng, tất cả các bộ điều khiển cuối cùng nhận được thứ gì đó không dành cho họ :(.

Vậy, làm thế nào để tránh vấn đề của next () ?

Câu trả lời thực sự đơn giản.

1- chỉ nên có một uri để xác định tài nguyên

http: // IpServidor / colection /: resource / colection /: resource nếu URI của bạn dài hơn thế, bạn nên xem xét việc tạo một uri mới

Ví dụ http: // IpServidor / users / pepe / contact / contacto1

2-Tất cả các hoạt động trên tài nguyên này phải được thực hiện tôn trọng tính không thay đổi của các động từ http (get, post, put, xóa, ...) để cuộc gọi đến URI thực sự chỉ có một cách gọi

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

Thêm thông tin [ https://docs.microsoft.com/es-es/azure/arch architecture / best-primary / api-design # Organize-the-api-around-resource [[1 ]

Hãy xem mã! Việc triển khai cụ thể khiến chúng ta tránh sử dụng next ()!

Trong tệp 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 );

Trong tệp 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;

Bây giờ, tệp usersRoute.js thực hiện những gì một tệp có tên usersRoute dự kiến ​​sẽ làm, đó là quản lý các tuyến của URI / users /

// tập tin getUsersControll.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;

Theo cách này, bạn tránh sử dụng tiếp theo, bạn tách mã, bạn đạt được hiệu suất, bạn phát triển RẮN, bạn để mở cửa cho việc di chuyển có thể sang microservice và trên hết, rất dễ đọc bởi một lập trình viên.


2
Điều này không chính xác, app.get sẽ không chuyển đến app.put như bạn đề xuất. Chỉ các yêu cầu phù hợp mới được gọi vì vậy nếu phương thức NHẬN thì chỉ phần mềm trung gian app.get sẽ được gọi. Phần mềm trung gian không cần kiểm tra phương thức yêu cầu. Đề xuất của bạn bỏ qua một chức năng chính của express và thay vào đó thực hiện định tuyến của riêng bạn. Hơn nữa, đề xuất của bạn cho rằng bạn định tuyến là phần mềm trung gian duy nhất bạn sẽ sử dụng vì nó không bao giờ được chuyển qua bất cứ đâu.
Ravenex

Thông tin không chính xác, xin vui lòng xem câu trả lời ở trên.
DDihua

-3

Kế tiếp() :

Gọi chức năng này gọi chức năng trung gian tiếp theo trong ứng dụng. Hàm next () không phải là một phần của Node.js hoặc Express API, nhưng là đối số thứ ba được truyền cho hàm phần mềm trung gian.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.