Когда использовать 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('/')vs. return res.redirect('/')в подобной ситуации? Может, лучше всегда писать return перед операторами res, чтобы избежать ошибок установки заголовков после их отправки?
Adam D

187

Как ответил @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('/')vs. return res.redirect('/')в подобной ситуации? Может просто лучше всегда писать returnперед resоператорами, чтобы избежать ошибок установки заголовков после их отправки?
Adam D

1
Зачем писать код после next ()? Разве не очевидно, что я ничего не делаю после завершения своей задачи в промежуточном программном обеспечении? @PJCHENder
Имран Поллоб

1
@ImranPollob иногда случаются ошибки. Когда вы пишете много кода, ifs / elses / etc. Вы можете забыть `` return next () ''
Йоне Полвора

46

next()является частью промежуточного программного обеспечения Connect . Обратным вызовам для потока маршрутизатора все равно, вернете ли вы что-нибудь из своих функций, поэтому 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
"так что return next () и next (); return; в основном то же самое". - именно то, что мне нужно было прочитать. спасибо @drinchev
Ник Пинеда

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

0

Лучше вообще не использовать! Я объясняю, и это то, что я тоже объясняю.

Функция next (), которая может иметь любое имя и по соглашению установлена ​​на 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, который является следующим маршрутом в файле 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 всем контроллерам, надеясь, что есть тот, который делает то, что вы хотите, посредством проверки req. В конце концов, все контроллеры получают то, что им не подходит :(.

Итак, как избежать проблемы с next () ?

Ответ действительно прост.

1- должен быть только один uri для идентификации ресурса

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 visible [1 ]

Посмотрим код! Конкретная реализация, которая заставляет нас избегать использования 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;

Таким образом, вы избегаете использования next, вы разделяете код, вы повышаете производительность, вы разрабатываете SOLID, вы оставляете дверь открытой для возможного перехода на микросервисы и, прежде всего, его легко читать программисту.


2
Это неверно, app.get не перейдет в app.put, как вы предлагаете. Вызываются только совпадающие запросы, поэтому, если используется метод GET, будет вызываться только промежуточное ПО app.get. Промежуточному программному обеспечению не нужно проверять метод запроса. Ваше предложение игнорирует основную функцию express и вместо этого реализует вашу собственную маршрутизацию. Кроме того, ваше предложение предполагает, что ваш маршрут является единственным промежуточным программным обеспечением, которое вы будете использовать, поскольку оно никогда никуда не передается.
Ravenex,

Неверная информация, см. Ответы выше.
DDiamond

-3

Следующий() :

Вызов этой функции вызывает следующую функцию промежуточного программного обеспечения в приложении. Функция next () не является частью Node.js или Express API, но является третьим аргументом, который передается функции промежуточного программного обеспечения.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.