Quando usar next () e retornar next () no Node.js

136

Cenário : considere o seguinte é a parte do código de um aplicativo da web do nó.

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

Problema : estou verificando qual deles deve ser usado com apenas next()ou return next(). O código de exemplo acima funciona exatamente da mesma forma para os dois e não mostrou nenhuma diferença na execução.

Pergunta : Alguém pode esclarecer isso, quando usar next()e quando usar return next()e alguma diferença importante?

Amol M Kulkarni
fonte

Respostas:

141

Algumas pessoas sempre escrevem return next()para garantir que a execução seja interrompida após o acionamento do retorno de chamada.

Se você não fizer isso, corre o risco de acionar o retorno de chamada uma segunda vez depois, o que geralmente tem resultados devastadores. Seu código está correto, mas eu o reescreveria como:

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

    if(!id)
        return next();

    // do something
});

Isso economiza um nível de indentação e, quando leio o código novamente mais tarde, tenho certeza de que não há como nextchamar duas vezes.

Laurent Perrin
fonte
2
Seria o mesmo com res.redirect('/')vs. return res.redirect('/')neste tipo de situação? Talvez seja melhor sempre escrever retorno na frente de instruções res para evitar erros na definição de cabeçalhos após o envio?
Adam D
185

Como resposta de @Laurent Perrin:

Se você não fizer isso, corre o risco de acionar o retorno de chamada uma segunda vez depois, o que geralmente tem resultados devastadores

Eu dou um exemplo aqui se você escrever middleware como este:

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

Você descobrirá que a saída no console é:

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

Ou seja, ele executa o código abaixo next () após a conclusão de todas as funções do middleware.

No entanto, se você usar return next(), ele retornará o retorno de chamada imediatamente e o código abaixo return next()no retorno de chamada estará inacessível.

PJCHENder
fonte
29
Como iniciante expressnessa resposta, as coisas ficaram mais claras para mim do que as outras respostas. Afirmativo!
tangerina
1
Seria o mesmo com res.redirect('/')vs. return res.redirect('/')neste tipo de situação? Talvez seja melhor sempre escrever returnna frente das resinstruções para evitar erros na definição dos cabeçalhos após o envio?
Adam D
1
Por que eu deveria escrever código depois de next ()? Não é óbvio que não faço nada depois de concluir minha tarefa em um middleware? @PJCHENder
Imran Pollob
1
@ImranPollob às vezes erros acontecem. Quando você escreve muito código, ifs / elses / etc. Você pode esquecer `` return next () `'
Jone Polvora
46

next()faz parte do connect middleware . Faz chamadas de retorno para o fluxo de router não me importo se você retornar qualquer coisa de suas funções, de modo return next()enext(); return; é basicamente o mesmo.

Caso queira interromper o fluxo de funções, você pode usar next(err)como o seguinte

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

Praticamente next()é usado para estender o middleware de suas solicitações.

drinchev
fonte
1
Podemos enviar parâmetros como next('No ID'):?
Amol M Kulkarni
7
next('No ID')está realmente enviando um erro, o que interromperá o fluxo.
Drinchev 29/05
Use next (null, "somevalue"); Para ferramentas como async.waterfall, ele passará o valor para a próxima função. Para séries complexas de interações orientadas por dados, geralmente passo um objeto de contexto entre funções. Dessa forma eu posso criar funções genéricas que podem ser compartilhados entre vários pontos finais e fluxo de controle através de dados no contexto
Chad Wilson
5
"então retorne next () e next (); return; é basicamente o mesmo." - exatamente o que eu precisava ler. thx @drinchev
Nick Pineda
1
Observo o oposto (ao disparar um erro): next (error) aciona o próximo middleware, mas continua executando o código; return next (erro) apenas relega a execução para o próximo middleware. próximo (e) e retornar próximo (e) NÃO são os mesmos.
Nickolodeon
0

É melhor não usá-lo! Eu explico, e é isso que eu também explico.

A função next () que pode ter qualquer nome e por convenção foi definida como next. Está indiretamente relacionado às operações (PUT, GET, DELETE, ...) geralmente executadas no mesmo recurso URI, por exemplo/ 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 ()...)

Agora, se você olhar para app.get, app.put e app.delete, use a mesma uri (/ user /: id), a única coisa que os diferencia é sua implementação. Quando a solicitação é feita (req) express, a primeira é solicitada em app.get, se alguma validação criada porque essa solicitação não é para o controlador falhar, ela passa a req para app.put, que é a próxima rota no arquivo te e, portanto, em. Como visto no exemplo abaixo.

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

   })

O problema é que, no final, você acaba passando a requisição para todos os controladores, esperando que exista uma que faça o que você deseja, através da validação da requisição. No final, todos os controladores acabam recebendo algo que não é para eles :(.

Então, como evitar o problema de next () ?

A resposta é realmente simples.

1- deve haver apenas uma uri para identificar um recurso

http: // IpServidor / colection /: resource / colection /: resource se o seu URI for maior que isso, considere criar um novo URL

Exemplo http: // IpServidor / users / pepe / Contacts / contacto1

2-Todas as operações neste recurso devem ser feitas respeitando a idempotência dos verbos http (get, post, put, delete, ...) para que a chamada para um URI realmente tenha apenas uma maneira de chamar

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

Mais informações [ https://docs.microsoft.com/es-es/azure/architecture/best-practices/api-design#organize-the-api-around-resources[[1]

Vamos ver o código!A implementação concreta que nos faz evitar o uso de next ()!

No arquivo 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 );

No arquivo 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;

Agora, o arquivo usersRoute.js faz o que um arquivo chamado usersRoute deve fazer, que é gerenciar as rotas do URI / users /

// arquivo 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;

Dessa forma, evita-se o uso do próximo, você dissocia o código, obtém desempenho, desenvolve o SOLID, deixa a porta aberta para uma possível migração para microsserviços e, acima de tudo, é fácil de ler por um programador.

David
fonte
2
Isso está incorreto, app.get não passará para app.put como você sugere. Somente solicitações correspondentes são chamadas, portanto, se o método for GET, apenas o middleware app.get será chamado. O middleware não precisa verificar o método de solicitação. Sua sugestão ignora a função principal de expressar e implementa seu próprio roteamento. Além disso, sua sugestão assume que a rota é o único middleware que você usará, pois nunca será transmitido a lugar algum.
Ravenex
Informações incorretas, consulte as respostas acima.
DDiamond 14/06
-3

Próximo() :

A chamada dessa função chama a próxima função de middleware no aplicativo. A função next () não faz parte da API Node.js ou Express, mas é o terceiro argumento que é passado para a função middleware.

Manish sharma
fonte