Kapan menggunakan next () dan return next () di Node.js

136

Skenario : Pertimbangkan yang berikut ini adalah bagian dari kode dari aplikasi web node.

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

Masalah : Saya sedang memeriksa yang mana yang cocok dengan hanya next()atau return next(). Kode contoh di atas bekerja persis sama untuk keduanya & tidak menunjukkan perbedaan dalam eksekusi.

Pertanyaan : Dapatkah seseorang menjelaskan hal ini, kapan harus digunakan next()dan kapan harus digunakan return next()dan beberapa perbedaan penting?

Amol M Kulkarni
sumber

Jawaban:

141

Beberapa orang selalu menulis return next()adalah untuk memastikan bahwa eksekusi berhenti setelah memicu panggilan balik.

Jika Anda tidak melakukannya, Anda berisiko memicu panggilan balik untuk kedua kalinya kemudian, yang biasanya memiliki hasil yang menghancurkan. Kode Anda baik-baik saja, tetapi saya akan menulis ulang sebagai:

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

    if(!id)
        return next();

    // do something
});

Ini menyelamatkan saya tingkat indentasi, dan ketika saya membaca kode lagi nanti, saya yakin tidak ada cara nextyang disebut dua kali.

Laurent Perrin
sumber
2
Apakah hal yang sama berlaku untuk res.redirect('/')vs return res.redirect('/')dalam situasi seperti ini? Mungkin lebih baik untuk selalu menulis balasan di depan pernyataan res untuk menghindari kesalahan pengaturan header setelah dikirim?
Adam D
187

Sebagai jawaban @Laurent Perrin:

Jika Anda tidak melakukannya, Anda berisiko memicu panggilan balik untuk kedua kalinya kemudian, yang biasanya memiliki hasil yang menghancurkan

Saya memberi contoh di sini jika Anda menulis middleware seperti ini:

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

Anda akan mengetahui bahwa output di konsol adalah:

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

Artinya, ia menjalankan kode di bawah next () setelah semua fungsi middleware selesai.

Namun, jika Anda menggunakan return next(), itu akan segera keluar dari panggilan balik dan kode return next()di bawah di panggilan balik itu tidak dapat dijangkau.

PJCHENder
sumber
29
Sebagai pemula untuk expressjawaban ini membuat segalanya lebih jelas bagi saya daripada jawaban lainnya. Jempolan!
mandarin
1
Apakah hal yang sama berlaku untuk res.redirect('/')vs return res.redirect('/')dalam situasi seperti ini? Mungkin lebih baik untuk selalu menulis returndi depan respernyataan untuk menghindari kesalahan mengatur tajuk setelah dikirim?
Adam D
1
Mengapa saya harus menulis kode setelah next ()? Bukankah sudah jelas bahwa saya tidak melakukan apa-apa setelah menyelesaikan tugas saya di middleware? @PJCHENder
Imran Pollob
1
@ImranPollob terkadang terjadi kesalahan. Ketika Anda menulis banyak kode, ifs / elses / etc. Anda mungkin lupa `` `kembali berikutnya ()`
Jone Polvora
46

next()adalah bagian dari connect middleware . Callback untuk aliran router tidak peduli jika Anda mengembalikan sesuatu dari fungsi Anda, jadi return next()dan next(); return;pada dasarnya sama.

Jika Anda ingin menghentikan aliran fungsi yang dapat Anda gunakan next(err)seperti berikut ini

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

Cukup banyak next()digunakan untuk memperluas middleware permintaan Anda.

drinchev
sumber
1
Bisakah kita mengirim parameter seperti: next('No ID')?
Amol M Kulkarni
7
next('No ID')sebenarnya mengirim kesalahan, yang akan memutus aliran.
drinchev
Gunakan berikutnya (nol, "nilai"); Untuk alat seperti async.waterfall, ia akan meneruskan nilai ke fungsi berikutnya. Untuk serangkaian interaksi kompleks yang didorong oleh data, saya biasanya melewatkan objek konteks antara fungsi. Dengan cara itu saya dapat membuat fungsi-fungsi umum yang dapat dibagikan melintasi banyak titik akhir dan mengontrol aliran melalui data dalam konteks
Chad Wilson
5
"jadi return next () dan next (); return; pada dasarnya sama." - hanya apa yang perlu saya baca. thx @drinchev
Nick Pineda
1
Saya mengamati sebaliknya (ketika firing error): next (error) memicu middleware berikutnya, tetapi terus mengeksekusi kode; return next (error) hanya membuang eksekusi ke middleware berikutnya. next (e) dan return next (e) TIDAK sama.
Nickolodeon
0

Yang terbaik adalah tidak menggunakannya sama sekali! Saya menjelaskan, dan itulah yang saya lakukan juga menjelaskannya.

Fungsi next () yang dapat memiliki nama apa pun dan dengan konvensi telah diatur ke berikutnya. Ini secara tidak langsung terkait dengan operasi (PUT, DAPATKAN, HAPUS, ...) yang umumnya dilakukan pada sumber daya URI yang sama misalnya/ 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 ()...)

Sekarang jika Anda melihat app.get, app.put dan app.delete menggunakan uri yang sama (/ user /: id), satu-satunya hal yang membedakan mereka adalah implementasinya. Ketika permintaan dibuat (req) express menempatkan req terlebih dahulu di app.get, jika validasi apa pun yang Anda buat karena permintaan itu bukan untuk pengontrol itu gagal, ia meneruskan req ke app.put yang merupakan rute selanjutnya dalam file te dan sebagainya di. Seperti yang terlihat pada contoh di bawah ini.

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

   })

Masalahnya terletak, bahwa pada akhirnya Anda akhirnya menyerahkan req ke semua pengendali berharap ada satu yang melakukan apa yang Anda inginkan, melalui validasi req. Pada akhirnya semua pengendali akhirnya menerima sesuatu yang bukan untuk mereka :(.

Jadi, bagaimana cara menghindari masalah next () ?

Jawabannya sangat sederhana.

1 - hanya harus ada satu uri untuk mengidentifikasi sumber daya

http: // IpServidor / colection /: resource / colection /: resource jika URI Anda lebih lama dari itu, Anda harus mempertimbangkan membuat uri baru

Contoh http: // IpServidor / pengguna / pepe / kontak / contacto1

2-Semua operasi pada sumber ini harus dilakukan dengan menghormati idempotence dari kata kerja http (get, post, put, delete, ...) sehingga panggilan ke URI benar-benar hanya memiliki satu cara panggilan

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

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

Mari kita lihat kodenya! Implementasi konkret yang membuat kita menghindari penggunaan next ()!

Dalam 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 );

Dalam 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;

Sekarang file usersRoute.js melakukan apa yang diharapkan dilakukan oleh file yang disebut usersRoute, yaitu mengelola rute URI / pengguna /

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

Dengan cara ini Anda menghindari penggunaan selanjutnya, Anda decouple kode, Anda mendapatkan kinerja, Anda mengembangkan SOLID, Anda membiarkan pintu terbuka untuk kemungkinan migrasi ke layanan Microsoft dan di atas semua itu, mudah dibaca oleh seorang programmer.

David
sumber
2
Ini tidak benar, app.get tidak akan meneruskan ke app.put seperti yang Anda sarankan. Hanya permintaan yang cocok dipanggil jadi jika metode ini DAPATKAN hanya app.get middleware akan dipanggil. Middleware tidak perlu memeriksa metode permintaan. Saran Anda mengabaikan fungsi utama pengekspresian dan mengimplementasikan perutean Anda sendiri. Lebih lanjut, saran Anda mengasumsikan bahwa rute Anda adalah satu-satunya middleware yang akan Anda gunakan karena tidak pernah dilewati di mana pun.
Ravenex
Informasi yang salah, silakan lihat jawaban di atas.
DDiamond
-3

Lanjut() :

Memanggil fungsi ini memanggil fungsi middleware berikutnya dalam aplikasi. Fungsi () berikutnya bukan bagian dari Node.js atau Express API, tetapi argumen ketiga yang diteruskan ke fungsi middleware.

Sharma Manish
sumber