Como redireciono no expressjs enquanto transmito algum contexto?

270

Estou usando o express para criar um aplicativo da web no node.js. Esta é uma simplificação do que tenho:

var express = require('express');
var jade = require('jade');
var http = require("http");


var app = express();
var server = http.createServer(app);

app.get('/', function(req, res) {
    // Prepare the context
    res.render('home.jade', context);
});

app.post('/category', function(req, res) {
    // Process the data received in req.body
    res.redirect('/');
});

Meu problema é o seguinte:

Se eu achar que os dados enviados /categorynão são validados, gostaria de passar algum contexto adicional para a /página. Como eu pude fazer isso? O redirecionamento não parece permitir nenhum tipo de parâmetro extra.

Tenda Enrique Moreno
fonte
1
Verifique o express ' flash: stackoverflow.com/questions/12442716/…
tymeJV
A terminologia é importante aqui: não há /página. Há uma /rota, que pode expressar serviço, e há um modelo de página inicial, que pode expressar. Se você deseja preservar alguns dados para renderizar o modelo da página inicial após um redirecionamento, a resposta aceita, não obstante, as sessões são suas amigas. Eles oferecem a você um armazenamento de dados que persiste em solicitações e respostas para usuários de navegação individuais, para que você possa inserir dados durante o /categorymanuseio e retirá-lo, se houver, durante o manuseio /.
Mike 'Pomax' Kamermans

Respostas:

368

Existem algumas maneiras de transmitir dados para diferentes rotas. A resposta mais correta é, é claro, as cadeias de consulta. Você precisará garantir que os valores sejam codificados corretamenteURIComponent e decodeURIComponent .

app.get('/category', function(req, res) {
  var string = encodeURIComponent('something that would break');
  res.redirect('/?valid=' + string);
});

Você pode pegar isso na sua outra rota obtendo os parâmetros enviados usando req.query.

app.get('/', function(req, res) {
  var passedVariable = req.query.valid;
  // Do something with variable
});

Para uma maneira mais dinâmica, você pode usar o urlmódulo principal para gerar a sequência de consultas para você:

const url = require('url');    
app.get('/category', function(req, res) {
    res.redirect(url.format({
       pathname:"/",
       query: {
          "a": 1,
          "b": 2,
          "valid":"your string here"
        }
     }));
 });

Portanto, se você deseja redirecionar todas as variáveis ​​de sequência de consulta de consulta, você pode simplesmente fazer

res.redirect(url.format({
       pathname:"/",
       query:req.query,
     });
 });

E se você estiver usando Nó> = 7.x, também poderá usar o querystringmódulo principal

const querystring = require('querystring');    
app.get('/category', function(req, res) {
      const query = querystring.stringify({
          "a": 1,
          "b": 2,
          "valid":"your string here"
      });
      res.redirect('/?' + query);
 });

Outra maneira de fazer isso é configurando algo na sessão. Você pode ler como configurá-lo aqui , mas configurar e acessar variáveis ​​é algo como isto:

app.get('/category', function(req, res) {
  req.session.valid = true;
  res.redirect('/');
});

E depois depois do redirecionamento ...

app.get('/', function(req, res) {
  var passedVariable = req.session.valid;
  req.session.valid = null; // resets session variable
  // Do something
});

Também há a opção de usar um recurso antigo do Express req.flash,. Para fazer isso nas versões mais recentes do Express, será necessário o uso de outra biblioteca. Essencialmente, ele permite que você configure variáveis ​​que serão exibidas e redefinidas na próxima vez que você acessar uma página. É útil para mostrar erros aos usuários, mas novamente foi removido por padrão. EDIT: Encontrou uma biblioteca que adiciona essa funcionalidade.

Espero que isso lhe dê uma idéia geral de como transmitir informações em um aplicativo Express.

AlbertEngelB
fonte
6
As consultas não se sentem muito bem em passar o contexto, pois isso pode ser tão grande quanto um parágrafo longo ou até mais. Quanto às sessões, isso pode funcionar. mas esquentar realmente se sentir bem, pois isso não parece o propósito certo de sessões ...
Enrique Moreno Tent
@Dbugger Opa, não percebi que você tinha feito a pergunta. Se você estiver preocupado em passar muitas informações para o usuário via string de consulta, talvez seja melhor armazenar os dados de validação em um banco de dados e depois consultá-los quando você atingir a /rota principal . Outra opção é chamar a rota de postagem via AJAX e armazenar os dados resultantes no armazenamento local ou algo semelhante.
AlbertEngelB
13
Boa resposta, mas acho que as sessões é a melhor abordagem do que as strings de consulta - não exponha coisas nos URLs!
user2562923
2
@ user2562923 Concordo que as sessões ou POSTs são melhores que os GETs para passar o contexto. A resposta foi bastante vaga, então eu não tinha certeza de que tipo de informação o solicitante estava passando.
AlbertEngelB 27/02
js nó tem 2 módulos principais que geram a string de consulta
Fareed Alnamrouti
42

A maneira mais fácil que encontrei de passar dados entre routeHandlers para next()não precisar mexer com redirecionamento ou sessões. Opcionalmente, você pode chamar o seu em homeCtrl(req,res)vez de next()e apenas passar o reqeres

var express  = require('express');
var jade     = require('jade');
var http     = require("http");


var app    = express();
var server = http.createServer(app);

/////////////
// Routing //
/////////////

// Move route middleware into named
// functions
function homeCtrl(req, res) {

    // Prepare the context
    var context = req.dataProcessed;
    res.render('home.jade', context);
}

function categoryCtrl(req, res, next) {

    // Process the data received in req.body
    // instead of res.redirect('/');
    req.dataProcessed = somethingYouDid;
    return next();
    // optionally - Same effect
    // accept no need to define homeCtrl
    // as the last piece of middleware
    // return homeCtrl(req, res, next);
}

app.get('/', homeCtrl);

app.post('/category', categoryCtrl, homeCtrl);
jqualls
fonte
4
Amigo, eu amo essa resposta. Me ajudou a sair de um buraco lá - embora eu realmente precise ler mais, pois meu intestino estava clamando para que eu passasse req e res como argumentos para next ().
precisa saber é o seguinte
1
Resposta muito boa e conservadora! Muito mais fácil e muito mais seguro do que as outras respostas.
Triforcey 25/05
2
Esta abordagem parece ser a mais limpa, mas não tenho certeza se ele vai deixou a barra de endereços na última caminho ou pelo se ele vai acabar/
Danielo515
1
@ Danielo515 isso vai acabar em / categorias, infelizmente, então isso é realmente o mesmo que apenas tornando a vista diretamente no / categorias ...
Manuel Graf
2
Não concordo que você esteja apenas usando o roteador como um serviço para renderizar uma visualização diferente, que deixará a URL como está; é uma má ideia estruturar seu código dessa maneira, o objetivo da próxima etapa é passar para o próximo middleware e geralmente isolar a nossa lógica de negócios da lógica routers
Fareed Alnamrouti
9

Eu tive que encontrar outra solução porque nenhuma das soluções fornecidas realmente atendeu aos meus requisitos, pelos seguintes motivos:

  • Cadeias de consulta : talvez você não queira usar cadeias de consulta porque os URLs podem ser compartilhados por seus usuários e, às vezes, os parâmetros de consulta não fazem sentido para um usuário diferente. Por exemplo, um erro como ?error=sessionExpirednunca deve ser exibido para outro usuário por acidente.

  • req.session : talvez você não queira usá- req.sessionlo porque precisa da dependência de sessão expressa para isso, que inclui a configuração de um armazenamento de sessão (como o MongoDB), que talvez você não precise, ou talvez já esteja usando um recurso personalizado. solução de armazenamento de sessão.

  • next () : você pode não querer usar next()ou next("router")porque isso essencialmente renderiza sua nova página no URL original, não é realmente um redirecionamento para o novo URL, mais como um encaminhamento / reescrita, o que pode não ser aceitável.

Portanto, esta é a minha quarta solução que não sofre com nenhum dos problemas anteriores. Basicamente, envolve o uso de um cookie temporário, para o qual você deverá primeiro instalar o analisador de cookies . Obviamente, isso significa que ele só funcionará onde os cookies estiverem ativados e com uma quantidade limitada de dados.

Exemplo de implementação:

var cookieParser = require("cookie-parser");

app.use(cookieParser());

app.get("/", function(req, res) {
    var context = req.cookies["context"];
    res.clearCookie("context", { httpOnly: true });
    res.render("home.jade", context); // Here context is just a string, you will have to provide a valid context for your template engine
});

app.post("/category", function(req, res) {
    res.cookie("context", "myContext", { httpOnly: true });
    res.redirect("/");
}
cprcrack
fonte
Não é necessário armazenamento de sessão express-session. Um banco de dados apenas faz as sessões persistirem entre as reinicializações do servidor.
Mike 'Pomax' Kamermans
6

use app.set & app.get

Configurando dados

router.get(
  "/facebook/callback",
  passport.authenticate("facebook"),
  (req, res) => {
    req.app.set('user', res.req.user)
    return res.redirect("/sign");
  }
);

Obtendo dados

router.get("/sign", (req, res) => {
  console.log('sign', req.app.get('user'))
});
UA_
fonte
2
má idéia para implementá-lo. isso pode afetar todos os usuários, todos os req.app são iguais para todos os usuários. podemos usar sessão expressa em vez disso.
ujjal das 10/03
5

Aqui está o que eu sugiro sem usar nenhuma outra dependência, apenas node e express, use app.locals, aqui está um exemplo:

    app.get("/", function(req, res) {
        var context = req.app.locals.specialContext;
        req.app.locals.specialContext = null;
        res.render("home.jade", context); 
        // or if you are using ejs
        res.render("home", {context: context}); 
    });

    function middleware(req, res, next) {
        req.app.locals.specialContext = * your context goes here *
        res.redirect("/");
    }
Hussein Dimessi
fonte
2
Sinto que essa é uma péssima idéia, porque req.app.locals parece afetar todos os usuários, não apenas aquele que fez a solicitação. Claro que você limpará os dados assim que passados ​​para o usuário, mas eu imagino que você ainda os tenha em conflito e exibindo informações incorretas de tempos em tempos. E se você tiver que limpá-lo de qualquer maneira, é melhor armazená-lo na sessão.
stackers
1
na verdade, ele somente efetuará a solicitação enviada pelos usuários "únicos", mesmo que 2 ou mais usuários enviem solicitações no "mesmo tempo", cada usuário terá seu próprio objeto de solicitação e seu objeto de locals, para que seja completamente seguro use
Hussein Dimessi 19/04/19
req é para um usuário único, mas req.app é para todos os usuários.
ZeroCho 22/12/19
use express-session para enviar a mensagem
ujjal das
3

Você pode transmitir pequenos bits de dados de pares de chave / valor através da string de consulta:

res.redirect('/?error=denied');

E o javascript na página inicial pode acessar isso e ajustar seu comportamento de acordo.

Observe que, se você não se importa em /categorypermanecer como o URL na barra de endereço do navegador, pode renderizar diretamente, em vez de redirecionar. O IMHO muitas vezes as pessoas usam redirecionamentos porque estruturas da Web mais antigas dificultam a resposta direta, mas é fácil de expressar:

app.post('/category', function(req, res) {

  // Process the data received in req.body

  res.render('home.jade', {error: 'denied'});
});

Como o @ Dropped.on.Caprica comentou, o uso do AJAX elimina a preocupação de alteração de URL.

Peter Lyons
fonte
Como eu disse em outra resposta, as cadeias de consulta não parecem boas, pois ainda não sei qual o tamanho dos dados que quero passar. Sua outra solução é a mais agradável que encontrei, mas ainda assim interrompe minha intenção de manter a mesma URL.
Enrique Moreno Tent
@Dbugger Você pode usar um AJAX POST?
AlbertEngelB
1

podemos usar sessão expressa para enviar os dados necessários

quando você inicializa o aplicativo

const express = require('express');
const app = express();
const session = require('express-session');
app.use(session({secret: 'mySecret', resave: false, saveUninitialized: false}));

então, antes do redirecionamento, salve o contexto da sessão

app.post('/category', function(req, res) {
    // add your context here 
req.session.context ='your context here' ;
    res.redirect('/');
});

Agora você pode obter o contexto em qualquer lugar da sessão. ele pode ser obtido apenas por req.session.context

app.get('/', function(req, res) {

    // So prepare the context
var context=req.session.context;
    res.render('home.jade', context);
});
ujjal das
fonte