Для чого використовується параметр "Наступний" в Express?


295

Припустимо, у вас є такий простий блок коду, як цей:

app.get('/', function(req, res){
    res.send('Hello World');
});

Ця функція має два параметри reqі res, які представляють об'єкти запиту та відповіді відповідно.

З іншого боку, є й інші функції з третім параметром, що називається next. Наприклад, давайте подивіться на наступний код:

app.get('/users/:id?', function(req, res, next){ // Why do we need next?
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); // What is this doing?
    }
});

Я не можу зрозуміти, в чому next()полягає сенс , або для чого його використовують. У цьому прикладі, якщо ідентифікатора не існує, що nextнасправді робиться ?


13
Далі просто дозволяє наступному оброблювачу маршруту в черзі обробляти запит. У цьому випадку, якщо ідентифікатор користувача існує, він, ймовірно, використовуватиме res.sendдля завершення запиту. Якщо його не існує, ймовірно, інший обробник видасть помилку і завершить запит.
Домінік Барнс

1
тож ти кажеш, що у мене є app.post('/login',function(req,res))після app.get('/users',function(req,res)) того, як він викличе логін наступним маршрутом у файлі app.js, зателефонувавши next ()?
Менцтруаль

2
Ні, вам слід посилатися на цю частину документації Express.js: expressjs.com/guide.html#passing-route control
Домінік Барнс

3
По суті, наступний маршрут, який буде запущений, буде ще одним маршрутом, який відповідає URL-адресу запиту. У цьому випадку, якщо інший маршрут був зареєстрований через app.get("/users"), він буде запущений, якщо обробник вище викликає наступний.
Домінік Барнс

3
Далі - це лише зворотний виклик.
Джонатан Онг,

Відповіді:


266

Він передає контроль наступному маршруту узгодження . У прикладі, який ви даєте, наприклад, ви можете шукати користувача в базі даних, якщо він idбув наданий, і призначити його req.user.

Нижче, у вас може бути такий маршрут, як:

app.get('/users', function(req, res) {
  // check for and maybe do something with req.user
});

Оскільки / users / 123 спочатку відповідатимуть маршруту у вашому прикладі, він спочатку перевірить і знайде користувача 123; то /usersможе щось зробити з результатом цього.

Посереднє програмне забезпечення маршруту є більш гнучким і потужним інструментом, хоча, на мою думку, оскільки воно не покладається на певну схему URI або впорядкування маршруту. Я би схильний моделювати такий приклад, припускаючи Usersмодель з асинхронією findOne():

function loadUser(req, res, next) {
  if (req.params.userId) {
    Users.findOne({ id: req.params.userId }, function(err, user) {
      if (err) {
        next(new Error("Couldn't find user: " + err));
        return;
      }

      req.user = user;
      next();
    });
  } else {
    next();
  }
}

// ...

app.get('/user/:userId', loadUser, function(req, res) {
  // do something with req.user
});

app.get('/users/:userId?', loadUser, function(req, res) {
  // if req.user was set, it's because userId was specified (and we found the user).
});

// Pretend there's a "loadItem()" which operates similarly, but with itemId.
app.get('/item/:itemId/addTo/:userId', loadItem, loadUser, function(req, res) {
  req.user.items.append(req.item.name);
});

Вміти керувати потоком, як це, дуже зручно. Можливо, ви хочете, щоб певні сторінки були доступні лише користувачам, які мають прапор адміністратора:

/**
 * Only allows the page to be accessed if the user is an admin.
 * Requires use of `loadUser` middleware.
 */
function requireAdmin(req, res, next) {
  if (!req.user || !req.user.admin) {
    next(new Error("Permission denied."));
    return;
  }

  next();
}

app.get('/top/secret', loadUser, requireAdmin, function(req, res) {
  res.send('blahblahblah');
});

Сподіваюся, це дало вам натхнення!


Ви можете сказати це! Я б більше схильний робити подібні речі з проміжним програмним забезпеченням маршруту , оскільки це не поєднує логіку з певним порядком маршрутів або певними структурами URI.
Ашера

5
чому іноді повернутися в наступному () , але іноді не
Джон

6
@John: повернене значення фактично ігнорується; Я просто хочу повернутися туди, щоб переконатися, що я більше не дзвоню next(). Було б те саме, якби я щойно використовував next(new Error(…)); return;.
Ашера

1
@ level0: повернене значення ігнорується; ви можете вважати це скороченим next(new Error(…)); return;. Якщо ми передамо значення next, це в односторонньому порядку вважається помилкою . Я не надто заглядав у експрес-код, але копайтеся і ви знайдете те, що вам потрібно :)
Asherah

1
@ Level0: (я змінив return next(…);до next(…); return;так це менш заплутаним.)
Ашера

87

У мене також були проблеми з розумінням наступного (), але це допомогло

var app = require("express")();

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write("Hello");
    next(); //remove this and see what happens 
});

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write(" World !!!");
    httpResponse.end();
});

app.listen(8080);

3
Дуже лаконічно! Дякую! Але як переконатися, що перший .getназивається, а не другий?
JohnnyQ

18
@JohnnyQ Це буде зверху донизу
Tapash

59

Перш ніж зрозуміти next, вам потрібно мати трохи уявлення про цикл запит-відповідь у вузлі, хоча і не дуже детально. Він починається з того, що ви робите запит HTTP на певний ресурс, і закінчується, коли ви надсилаєте відповідь користувачеві, тобто коли ви стикаєтесь з чимось на зразок res.send ("Hello World");

давайте подивимося на дуже простий приклад.

app.get('/hello', function (req, res, next) {
  res.send('USER')
})

Тут нам не потрібен наступний (), оскільки resp.send закінчить цикл і передасть керування назад в програмне забезпечення маршруту.

Тепер давайте розглянемо інший приклад.

app.get('/hello', function (req, res, next) {
  res.send("Hello World !!!!");
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

Тут ми маємо 2 функції середнього програмного забезпечення для того ж шляху. Але ви завжди отримаєте відповідь з першого. Тому що це встановлено спочатку в стек середнього програмного забезпечення, а res.send закінчить цикл.

Але що робити, якщо ми завжди не хочемо "Привіт Світу !!!!" відповідь назад. Для деяких умов ми можемо захотіти "Привіт планети !!!!" відповідь. Давайте модифікуємо вищевказаний код і подивимося, що відбувається.

app.get('/hello', function (req, res, next) {
  if(some condition){
    next();
    return;
  }
  res.send("Hello World !!!!");  
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

Що nextтут робиться. І так, у вас можуть бути гази. Перша функція середнього програмного забезпечення буде пропущена, якщо умова є правдою, і викличе наступну функцію середнього програмного забезпечення, і ви отримаєте "Hello Planet !!!!"відповідь.

Отже, далі передайте керування наступній функції в стеку проміжного програмного забезпечення.

Що робити, якщо перша функція середнього програмного забезпечення не повертає жодної відповіді, але виконує частину логіки, і тоді ви отримаєте відповідь від другої функції середнього програмного забезпечення.

Щось на кшталт нижче: -

app.get('/hello', function (req, res, next) {
  // Your piece of logic
  next();
});

app.get('/hello', function (req, res, next) {
  res.send("Hello !!!!");
});

У цьому випадку вам потрібно викликати обидві функції проміжного програмного забезпечення. Отже, єдиний спосіб досягти другої функції середнього програмного забезпечення - зателефонувавши next ();

Що робити, якщо ви не телефонуєте до наступного. Не сподівайтеся, що друга функція середнього програмного забезпечення буде автоматично викликана. Після виклику першої функції ваш запит буде залишений. Друга функція ніколи не буде викликана, і ви не отримаєте відповідь.


Так next()виконує, як gotoз жорстким проводом етикетки? Тобто, у вашому третьому фрагменті, коли ви телефонуєте next(), res.send("Hello World !!!!"); ніколи не буде виконано? Я помітив , що @Ashe завжди був return;після nextдзвінків , які мали код в тому ж дереві виконання ... Здається , я завжди міг просто перевірити в експресі, так? / підбігає до свого текстового редактора;)
ruffin

@ruffin так, ви можете придумати наступне схоже на гото. але далі знає, куди звернутися на відміну від goto, який вимагає етикетки. Далі перейде управління до наступної функції середнього програмного забезпечення. Крім того, ви можете назвати "наступний" все, що завгодно. Тут просто етикетка. Але найкраща практика - використовувати назву "наступний"
Mav55

3
Гаразд, схоже, це не точно. Я спробував код ( пастибін тут ), і код після next()дзвінка викликається . У цьому випадку past the next() callзаписується на консоль, і тоді я отримую Error: Can't set headers after they are sent.помилку, як res.sendназивається друга , хоча і безуспішно. Потік коду повертається після next()виклику, що робить returnsважливим (або іншим логічним управлінням) @ Ashe важливим.
рюш

4
@ruffin, так, ти маєш рацію. Нам потрібен оператор повернення після того, next()як пропустити виконання решти операторів. дякую, що вказали на це.
Mav55

1
Дякуємо, що насправді пояснили, що таке "проміжне програмне забезпечення" / чи чіткі приклади, а не просто припущення документації. Це була єдина відповідь, яка насправді говорить про щось зрозуміле, що відбувається, чому і як.
mc01

11

Далі використовується для передачі управління наступній функції проміжного програмного забезпечення. Якщо ні, запит залишатиметься висячим або відкритим.


6
Я не впевнений, що ця відповідь додає майже до семирічного питання ...
mherzig

Хоча короткий, цей коментар привів мене до цього: expressjs.com/en/guide/writing-middleware.html
hmak

@mherzig, це один вкладиш і охоплює все
М. Гопал

5

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


2

Виконання nextфункції повідомляє сервер про те, що ви виконали цей крок проміжного програмного забезпечення, і він може виконати наступний крок у ланцюзі.

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