Коли використовувати next () і повернути 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

Тобто, він виконує код нижче next () після того, як буде виконана вся функція проміжного програмного забезпечення.

Однак, якщо ви користуєтесь return next(), вона негайно вискочить зворотний виклик, і код, наведений нижче, return next()у зворотному дзвінку буде недоступним.


29
Як початківець у expressцій відповіді мені зрозуміліші речі, ніж інші відповіді. Пальці вгору!
мандарин

1
Чи подібно до цього типу стосується res.redirect('/')порівняно return res.redirect('/')з цим типом ситуації? Може бути, краще завжди писати returnперед resтвердженнями, щоб уникнути помилок налаштування заголовків після їх надсилання?
Адам Д

1
Чому я повинен писати код після next ()? Хіба не очевидно, що я нічого не роблю після того, як закінчив своє завдання в проміжних програмах? @PJCHENder
Імран Поллоб

1
@ImranPollob іноді трапляються помилки. Коли ви пишете багато коду, ifs / elses / тощо. Ви можете забути `` `return next ()`
Jone Polvora

46

next()є частиною підключення проміжного програмного забезпечення . Callbacks для потоку маршрутизатора не хвилює , якщо ви повертаєте що - небудь з ваших функцій, так 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')насправді надсилає помилку, яка порушить потік.
Дрінчев

Використовувати next (null, "somevalue"); Для таких інструментів, як async.waterfall воно передасть значення наступній функції. Для складних серій взаємодій, керованих даними, я зазвичай передаю контекстний об'єкт між функціями. Таким чином я можу створити загальні функції, які можна розділити на декілька кінцевих точок та контролювати потік через дані в контексті
Чад Вілсон,

5
"так повертайтеся next () та next (); return; в основному те саме." - саме те, що мені потрібно було прочитати. thx @drinchev
Нік Пінеда

1
Я спостерігаю протилежне (при помилці запуску): next (помилка) запускає наступне проміжне програмне забезпечення, але продовжує виконувати код; return next (помилка) просто передає виконання наступному середньому програмному забезпеченню. next (e) та return next (e) НЕ є однаковим.
Nickolodeon

0

Найкраще його взагалі не використовувати! Я пояснюю, і це я теж пояснюю.

Наступна () функція, яка може мати будь-яке ім'я та за умовами, була встановлена ​​на next. Це опосередковано пов'язане з операціями (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, який є наступним маршрутом у файлі, і так на. Як видно з прикладу нижче.

    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 всім контролерам, сподіваючись, що існує одна, яка робить те, що ви хочете, через валідацію req. Зрештою, всі контролери в кінцевому підсумку отримують щось, що не для них :(.

Отже, як уникнути проблеми next () ?

Відповідь дійсно проста.

1 - для ідентифікації ресурсу повинен бути лише один урі

http: // IpServidor / colection /: resource / colection /: resource, якщо ваш URI довший, ніж вам, слід розглянути можливість створення нового uri

Приклад http: // IpServidor / users / pepe / Contacts / contacto1

2-Усі операції на цьому ресурсі повинні бути виконані з дотриманням ідентичності повноважень дієслів http (get, post, put, delete, ...), тому виклик до 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 freedict1]

Давайте подивимось код! Конкретна реалізація, яка змушує нас уникати використання next ()!

У файлі 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, залишаєте двері відкритими для можливої ​​міграції до мікросервісів і, перш за все, програміст легко читає.


2
Це неправильно, app.get не перейде до app.put, як ви пропонуєте. Викликаються лише відповідні запити, якщо метод GET отримує лише середнє програмне забезпечення app.get. Проміжне програмне забезпечення не потрібно перевіряти метод запиту. Ваша пропозиція ігнорує основну функцію експресу та реалізує натомість власну маршрутизацію. Крім того, ваша пропозиція передбачає, що маршрут - це єдине проміжне програмне забезпечення, яке ви будете використовувати, оскільки він ніде не проходить.
Ravenex

Неправильна інформація, див. Відповіді вище.
DDiamond

-3

Далі ():

Виклик цієї функції викликає наступну функцію програмного забезпечення в додатку. Наступна () функція не є частиною Node.js або Express API, але є третім аргументом, який передається функції середнього програмного забезпечення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.