Какой параметр «следующий» используется в 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()или почему его используют. В этом примере, если id не существует, что на nextсамом деле делает?

Menztrual
источник
13
Next просто позволяет следующему обработчику маршрута в строке обрабатывать запрос. В этом случае, если идентификатор пользователя существует, он, скорее всего, будет использоваться res.sendдля завершения запроса. Если он не существует, вероятно, есть другой обработчик, который выдаст ошибку и завершит запрос.
Доминик Барнс
1
так что вы говорите, что у меня есть app.post('/login',function(req,res))после app.get('/users',function(req,res)) того, как он вызовет вход в систему в качестве следующего маршрута в файле app.js, вызвав next ()?
Menztrual
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
почему иногда вы возвращаетесь next (), а иногда нет
Джон
6
@John: возвращаемое значение фактически игнорируется; Я просто хочу вернуться туда, чтобы не звонить next()снова. Было бы то же самое, если бы я просто использовал next(new Error(…)); return;.
Ашера
1
@ level0: возвращаемое значение игнорируется; Вы можете считать это сокращением для next(new Error(…)); return;. Если мы передаем значение next, это в одностороннем порядке считается ошибкой . Я не слишком много заглядывал в экспресс-код, но покопайтесь, и вы найдете то, что вам нужно :)
Ашера,
1
@ level0: (Я изменился return next(…);на next(…); return;так, что это менее запутанно.)
Ашера
87

У меня также были проблемы с пониманием next (), но это помогло

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);
rajesk
источник
3
Очень лаконично! Спасибо! Но как убедиться, что первый .getназывается, а не второй?
JohnnyQ
18
@JohnnyQ Это будет исполнение сверху вниз
Tapash
@JohnnyQ Маршруты подбираются так, как они объявлены. Если есть два совпадающих маршрута, будет выбран тот, который встречается первым в потоке программы.
Шубхам Ананд
59

Перед тем, как понять next, вам необходимо иметь представление о цикле «запрос-ответ» в узле, хотя и не очень подробно. Он начинается с того, что вы делаете HTTP-запрос для определенного ресурса, и заканчивается, когда вы отправляете ответ обратно пользователю, т.е. когда вы сталкиваетесь с чем-то вроде res.send ('Hello World');

давайте посмотрим на очень простой пример.

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

Здесь нам не нужна функция next (), так как 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 завершит цикл.

Но что, если мы всегда не хотим «Hello World !!!!» ответ обратно. Для некоторых условий мы можем хотеть "Привет Планета !!!!" ответ. Давайте изменим приведенный выше код и посмотрим, что произойдет.

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 !!!!"ответ.

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

Что если первая функция промежуточного программного обеспечения не отправляет ответ, но выполняет часть логики, а затем вы получаете ответ от второй функции промежуточного программного обеспечения.

Что-то вроде ниже: -

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

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

В этом случае вам нужны обе функции промежуточного программного обеспечения. Таким образом, единственный способ получить доступ ко второй функции промежуточного программного обеспечения - это вызвать next ();

Что делать, если вы не позвоните следующим. Не ожидайте, что вторая функция промежуточного программного обеспечения будет вызвана автоматически. После вызова первой функции ваш запрос останется висеть. Вторая функция никогда не будет вызвана, и вы не получите ответ.

Mav55
источник
Так next()работает как gotoс жесткой этикеткой? То есть в вашем третьем фрагменте, когда вы позвоните next(), res.send("Hello World !!!!"); никогда не будет выполнен? Я заметил, что у @Ashe всегда были вызовы return;после next, у которых был код в одном и том же дереве выполнения ... Думаю, я всегда мог просто проверить экспресс, да? / переходит к своему текстовому редактору;)
ruffin
@ ruffin да, вы можете подумать о следующем родстве с goto. но следующий знает, куда идти, в отличие от goto, для которого требуется ярлык. Далее передаст управление следующей функции промежуточного программного обеспечения. Кроме того, вы можете назвать «следующий» как угодно. Это просто ярлык здесь. Но лучше всего использовать имя «следующий»
Mav55
3
Хорошо, похоже, это не точно. Я попробовал код ( вставьте сюда ), и код после next()вызова вызывается . В этом случае past the next() callзаписывается на консоль, и тогда я получаю сообщение Error: Can't set headers after they are sent.об ошибке, как второй res.sendназывается, хотя и безуспешно. Поток кода возвращается после next()вызова, что делает returnsважным @ Ashe (или другое логическое управление).
Ерф
4
@ ruffin, да, ты прав. Нам нужен оператор return после, next()чтобы пропустить выполнение остальных операторов. спасибо за указание на это.
Mav55
1
Спасибо за то, что вы на самом деле объясняете, что такое «промежуточное программное обеспечение» / делает с четкими примерами, а не просто попугая документации. Это был единственный ответ, который на самом деле ясно говорит о том, что происходит, почему и как.
mc01
11

Next используется для передачи управления следующей функции промежуточного программного обеспечения. Если нет, то запрос будет оставлен висеть или открыт.

Роник Рикеттс
источник
6
Я не уверен, что этот ответ добавляет к почти семилетнему вопросу ...
mherzig
Несмотря на краткость, этот комментарий привел меня к этому: expressjs.com/en/guide/writing-middleware.html
hmak
@mherzig, это один лайнер и охватывает все
М. Гопал
5

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

Бинайак Гури Шанкар
источник
2

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

AGrush
источник