Quando utilizzare next () e restituire next () in Node.js


136

Scenario : considerare quanto segue è la parte del codice da un'app Web del nodo.

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

Problema : sto controllando con quale scegliere solo next()o return next(). Il codice di esempio sopra funziona esattamente allo stesso modo per entrambi e non mostra alcuna differenza nell'esecuzione.

Domanda : Qualcuno può far luce su questo, quando usare next()e quando usare return next()e alcune importanti differenze?

Risposte:


141

Alcune persone scrivono sempre return next()per assicurarsi che l'esecuzione si interrompa dopo aver attivato la richiamata.

Se non lo fai, rischi di innescare la richiamata una seconda volta dopo, che di solito ha risultati devastanti. Il tuo codice va bene così com'è, ma lo riscrivo come segue:

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

    if(!id)
        return next();

    // do something
});

Mi fa risparmiare un livello di rientro e quando rileggo il codice più tardi, sono sicuro che non è possibile nextchiamare due volte.


2
Una cosa simile sarebbe vera per res.redirect('/')vs. return res.redirect('/')in questo tipo di situazione? Forse è solo meglio scrivere sempre return prima delle dichiarazioni res per evitare errori nell'impostare le intestazioni dopo che sono state inviate?
Adam D,

187

Come la risposta di @Laurent Perrin:

Se non lo fai, rischi di innescare la richiamata una seconda volta dopo, che di solito ha risultati devastanti

Faccio un esempio qui se scrivi middleware in questo modo:

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

Scoprirai che l'output nella console è:

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

Cioè, esegue il codice sotto next () al termine di tutte le funzioni del middleware.

Tuttavia, se lo si utilizza return next(), salterà immediatamente il callback e il codice riportato return next()di seguito nel callback sarà irraggiungibile.


29
Come principiante a expressquesta risposta mi ha reso le cose più chiare rispetto alle altre risposte. Pollice su!
mandarino,

1
Una cosa simile sarebbe vera per res.redirect('/')vs. return res.redirect('/')in questo tipo di situazione? Forse è solo meglio scrivere sempre returnprima delle resdichiarazioni per evitare errori nell'impostare le intestazioni dopo che sono state inviate?
Adam D,

1
Perché dovrei scrivere il codice dopo next ()? Non è ovvio che non faccio nulla dopo aver finito il mio compito in un middleware? @PJCHENder
Imran Pollob,

1
@ImranPollob a volte si verificano errori. Quando scrivi molto codice, ifs / elses / etc. Potresti dimenticare `` return next () `
Jone Polvora,

46

next()fa parte del middleware connect . Callback per il flusso di router non importa se si torna qualcosa dalle vostre funzioni, in modo return next()e next(); return;è fondamentalmente la stessa.

Nel caso in cui si desideri interrompere il flusso di funzioni, è possibile utilizzare next(err)come segue

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()viene utilizzato per estendere il middleware delle tue richieste.


1
Possiamo inviare parametri come next('No ID'):?
Amol M Kulkarni,

7
next('No ID')sta effettivamente inviando un errore, che interromperà il flusso.
drinchev,

Usa next (null, "somevalue"); Per strumenti come async.waterfall passerà il valore alla funzione successiva. Per complesse serie di interazioni basate sui dati, di solito passo un oggetto di contesto tra le funzioni. In questo modo posso creare funzioni generiche che possono essere condivise su più endpoint e controllare il flusso tramite i dati nel contesto
Chad Wilson,

5
"quindi return next () e next (); return; è sostanzialmente lo stesso." - proprio quello che dovevo leggere. thx @drinchev
Nick Pineda,

1
Osservo il contrario (quando si attiva l'errore): next (errore) attiva il middleware successivo, ma continua ad eseguire il codice; return next (errore) relega solo l'esecuzione al middleware successivo. next (e) e return next (e) NON sono gli stessi.
Nickolodeon,

0

È meglio non usarlo affatto! Spiego, ed è quello che faccio anche spiegarlo.

La funzione next () che può avere qualsiasi nome e per convenzione è stata impostata su next. È indirettamente correlato alle operazioni (PUT, GET, DELETE, ...) che vengono generalmente eseguite sulla stessa risorsa URI, ad esempio/ 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 ()...)

Ora se guardi app.get, app.put e app.delete usano lo stesso uri (/ user /: id), l'unica cosa che li differenzia è la loro implementazione. Quando la richiesta viene fatta (req) express inserisce prima il req in app.get, se una convalida creata perché quella richiesta non è per quel controller fallisce, passa il req ad app.put che è la prossima rotta nel file te e così sopra. Come visto nell'esempio seguente.

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

   })

Il problema sta nel fatto che alla fine si passa il req a tutti i controller sperando che ce ne sia uno che fa quello che si desidera, attraverso la convalida del req. Alla fine tutti i controller finiscono per ricevere qualcosa che non fa per loro :(.

Quindi, come evitare il problema di next () ?

La risposta è davvero semplice

1- dovrebbe esserci un solo uri per identificare una risorsa

http: // IpServidor / colection /: resource / colection /: resource se il tuo URI è più lungo di quello, dovresti prendere in considerazione la creazione di un nuovo uri

Esempio http: // IpServidor / users / pepe / contatti / contacto1

2-Tutte le operazioni su questa risorsa devono essere eseguite rispettando l'idempotenza dei verbi http (get, post, put, delete, ...), quindi la chiamata a un URI ha davvero solo un modo di chiamare

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

Maggiori informazioni [ https://docs.microsoft.com/es-es/azure/architecture/best-practices/api-design#organize-the-api-around-resources[[1]

Vediamo il codice! L'implementazione concreta che ci fa evitare l'uso di next ()!

Nel file 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 );

Nel file 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;

Ora il file usersRoute.js fa quello che dovrebbe fare un file chiamato usersRoute, che è quello di gestire le rotte dell'URI / users /

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

In questo modo si evita l'uso di next, si disaccoppia il codice, si guadagna in prestazioni, si sviluppa SOLID, si lascia la porta aperta per una possibile migrazione a microservizi e, soprattutto, è facile da leggere da un programmatore.


2
Questo non è corretto, app.get non passerà ad app.put come suggerisci. Vengono chiamate solo le richieste corrispondenti, quindi se il metodo è GET verrà chiamato solo il middleware app.get. Il middleware non deve controllare il metodo di richiesta. Il tuo suggerimento ignora una funzione primaria di express e implementa invece il tuo routing. Inoltre, il tuo suggerimento presuppone che il tuo percorso sia l'unico middleware che utilizzerai poiché non viene mai passato da nessuna parte.
Ravenex

Informazioni errate, vedere le risposte sopra.
DDiamond

-3

Il prossimo() :

La chiamata a questa funzione richiama la funzione middleware successiva nell'app. La funzione next () non fa parte di Node.js o Express API, ma è il terzo argomento che viene passato alla funzione middleware.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.