Qual é o parâmetro "next" usado no Express?

295

Suponha que você tenha um bloco de código simples como este:

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

Essa função possui dois parâmetros, reqe res, que representam os objetos de solicitação e resposta, respectivamente.

Por outro lado, existem outras funções com um terceiro parâmetro chamado next. Por exemplo, vamos dar uma olhada no seguinte código:

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

Não consigo entender qual é o objetivo next()ou por que está sendo usado. Nesse exemplo, se o ID não existir, o que nextrealmente está fazendo?

Menztrual
fonte
13
Próximo simplesmente permite que o próximo manipulador de rota na fila lide com a solicitação. Nesse caso, se o ID do usuário existir, ele provavelmente será usado res.sendpara concluir a solicitação. Se não existir, provavelmente existe outro manipulador que emitirá um erro e concluirá a solicitação.
Dominic Barnes
1
então você está dizendo que eu tenho um app.post('/login',function(req,res))depois app.get('/users',function(req,res)) que ele chamará o login como a próxima rota no arquivo app.js chamando next ()?
Menztrual
2
Não, você deve consultar esta parte da documentação do Express.js: expressjs.com/guide.html#passing-route control
Dominic Barnes
3
Basicamente, a próxima rota a ser executada será outra que corresponda à URL da solicitação. Nesse caso, se outra rota foi registrada via app.get("/users"), será executada se o manipulador acima chamar em seguida.
Dominic Barnes
3
Em seguida, é basicamente apenas um retorno de chamada.
Jonathan Ong

Respostas:

266

Passa o controle para a próxima rota correspondente . No exemplo que você fornece, por exemplo, você pode procurar o usuário no banco de dados se um idfoi fornecido e atribuí-lo a req.user.

Abaixo, você pode ter uma rota como:

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

Como / users / 123 corresponderá primeiro à rota no seu exemplo, que primeiro verificará e encontrará o usuário 123; então /userspode fazer algo com o resultado disso.

O middleware de rota é uma ferramenta mais flexível e poderosa, no entanto, na minha opinião, uma vez que não depende de um esquema URI ou de um pedido de rota específico. Eu estaria inclinado a modelar o exemplo mostrado assim, assumindo um Usersmodelo com uma assíncrona 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);
});

Ser capaz de controlar o fluxo dessa maneira é bastante útil. Você pode querer que determinadas páginas estejam disponíveis apenas para usuários com um sinalizador de administrador:

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

Espero que isso tenha lhe dado alguma inspiração!

Asherah
fonte
Você poderia dizer isso! No entanto, eu estaria mais inclinado a fazer esse tipo de coisa com o middleware de rota , pois ele não acopla a lógica a uma ordem específica de rotas ou estruturas URI específicas.
Asherah
5
por que às vezes você volta a seguir (), mas às vezes não
John
6
@ John: o valor de retorno é realmente ignorado; Só estou querendo voltar para lá para garantir que não ligo next()novamente. Seria o mesmo se eu apenas usasse next(new Error(…)); return;.
Asherah
1
@ level0: o valor retornado é ignorado; você pode considerar isso como abreviação de next(new Error(…)); return;. Se passarmos um valor para next, é considerado unilateralmente um erro . Eu não olhei para o código expressa muito, mas cavar em volta e você vai encontrar o que você precisa :)
Asherah
1
@ level0: (Eu mudei return next(…);para next(…); return;por isso é menos confuso.)
Asherah
87

Eu também tive problemas para entender next (), mas isso ajudou

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
fonte
3
Muito sucinto! Obrigado! Mas como você garante que o primeiro .getseja chamado e não o segundo?
JohnnyQ
18
@JohnnyQ Será cima para execução inferior
Tapash
59

Antes de entender next, você precisa ter uma pequena idéia do ciclo de solicitação-resposta no nó, embora não muito detalhadamente. Começa com você solicitando HTTP para um recurso específico e termina quando você envia uma resposta de volta ao usuário, ou seja, quando você encontra algo como res.send ('Hello World');

vamos dar uma olhada em um exemplo muito simples.

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

Aqui não precisamos de next (), porque resp.send encerrará o ciclo e entregará o controle de volta ao middleware da rota.

Agora vamos dar uma olhada em outro exemplo.

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

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

Aqui temos duas funções de middleware para o mesmo caminho. Mas você sempre obterá a resposta do primeiro. Porque isso é montado primeiro na pilha de middleware e o res.send encerrará o ciclo.

Mas e se nem sempre queremos o "Olá Mundo !!!!" resposta de volta. Para algumas condições, podemos querer o "Olá Planeta !!!!" resposta. Vamos modificar o código acima e ver o que acontece.

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 !!!!");
});

O que está nextfazendo aqui. E sim, você pode ter gusses. Irá pular a primeira função do middleware se a condição for verdadeira e chamar a próxima função do middleware e você terá a "Hello Planet !!!!"resposta.

Portanto, passe o controle para a próxima função na pilha de middleware.

E se a primeira função de middleware não enviar nenhuma resposta, mas executar uma parte da lógica e você receber a resposta da segunda função de middleware.

Algo como abaixo: -

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

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

Nesse caso, você precisa que ambas as funções do middleware sejam invocadas. Portanto, a única maneira de alcançar a segunda função do middleware é chamando next ();

E se você não ligar para o próximo. Não espere que a segunda função de middleware seja chamada automaticamente. Depois de invocar a primeira função, sua solicitação será deixada em suspenso. A segunda função nunca será chamada e você não receberá a resposta novamente.

Mav55
fonte
Então, next()funciona como um gotocom uma etiqueta com fio? Ou seja, no seu terceiro trecho, uma vez que você ligar next(), res.send("Hello World !!!!"); nunca seria executado? Percebi que o @Ashe sempre teve chamadas return;após o nextcódigo na mesma árvore de execução ... Acho que eu sempre podia fazer check-in expresso, não é? / corre para o seu editor de texto;)
ruffin
@ ruffin sim, você pode pensar em próximo a um goto. mas a seguir sabe para onde ir, ao contrário de goto, que requer um rótulo. Em seguida, passará o controle para a próxima função de middleware. Além disso, você pode nomear 'próximo' como desejar. É apenas um rótulo aqui. Mas a melhor prática é usar o nome 'next'
Mav55
3
Ok, parece que isso não é exato. Eu tentei o código ( pastebin aqui ) e o código após a next()chamada ser chamada . Nesse caso, ele past the next() callé gravado no console e, em seguida, recebo um Error: Can't set headers after they are sent.erro, como o segundo res.sendé chamado, embora sem êxito. O fluxo de código retorna após a next()chamada, o que torna importante o @ Ashe returns(ou outro gerenciamento lógico).
Ruffin
4
@ Ruffin, sim, você está certo. Precisamos de uma declaração de retorno após a next()para ignorar a execução das instruções restantes. obrigado por apontar isso.
Mav55
1
Obrigado por realmente explicar o que "middleware" é / com exemplos claros e não apenas repetir a documentação. Esta foi a única resposta que realmente diz algo claro sobre o que acontece, por que e como.
Mc01 # 25/19
11

Next é usado para passar o controle para a próxima função de middleware. Caso contrário, a solicitação será deixada suspensa ou aberta.

Ronique Ricketts
fonte
6
Eu não tenho certeza que esta resposta adiciona a uma questão quase sete anos de idade ...
mherzig
Embora breve, este comentário me levar a isso: expressjs.com/en/guide/writing-middleware.html
hmak
@mherzig, é um forro e cobre tudo
M. Gopal
5

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. A função next () pode ter o nome de qualquer coisa, mas por convenção é sempre chamada de "next".

Binayak Gouri Shankar
fonte
2

A execução da nextfunção notifica o servidor que você concluiu esta etapa do middleware e ela pode executar a próxima etapa da cadeia.

AGrush
fonte