¿Cómo redirecciono en expressjs mientras paso algún contexto?

270

Estoy usando express para hacer una aplicación web en node.js. Esta es una simplificación de lo que tengo:

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

Mi problema es el siguiente:

Si encuentro que los datos enviados /categoryno se validan, me gustaría pasar algún contexto adicional a la /página. ¿Cómo podría hacer esto? Redirect no parece permitir ningún tipo de parámetro adicional.

Tienda Enrique Moreno
fuente
1
Echa un vistazo a express ' flash: stackoverflow.com/questions/12442716/…
tymeJV
La terminología importa aquí: no hay /página. Hay una /ruta, que express puede servir, y hay una plantilla de página de inicio, que express puede representar. Si desea conservar algunos datos para representar la plantilla de la página de inicio después de una redirección, a pesar de la respuesta aceptada, las sesiones son su amigo. Le brindan un almacén de datos que persiste en todas las solicitudes y respuestas para usuarios de navegación individuales para que pueda ingresar datos durante el /categorymanejo y luego eliminarlos si están allí durante la /entrega.
Mike 'Pomax' Kamermans

Respuestas:

368

Hay algunas formas de pasar datos a diferentes rutas. La respuesta más correcta es, por supuesto, las cadenas de consulta. Deberá asegurarse de que los valores estén correctamente encodeURIComponent y decodeURIComponent .

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

Puede enganchar eso en su otra ruta al obtener los parámetros enviados mediante req.query.

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

Para una forma más dinámica, puede usar el urlmódulo central para generar la cadena de consulta por usted:

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

Entonces, si desea redirigir todas las variables de cadena de consulta de solicitud, simplemente puede hacer

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

Y si está usando Nodo> = 7.x también puede usar el querystringmódulo central

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

Otra forma de hacerlo es configurando algo en la sesión. Puede leer cómo configurarlo aquí , pero configurar y acceder a las variables es algo como esto:

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

Y más tarde después de la redirección ...

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

También existe la opción de usar una característica antigua de Express, req.flash. Hacerlo en versiones más nuevas de Express requerirá que uses otra biblioteca. Esencialmente, le permite configurar variables que se mostrarán y restablecerán la próxima vez que vaya a una página. Es útil para mostrar errores a los usuarios, pero nuevamente se ha eliminado de forma predeterminada. EDITAR: se encontró una biblioteca que agrega esta funcionalidad.

Con suerte, eso le dará una idea general de cómo pasar información en una aplicación Express.

AlbertEngelB
fuente
66
Querys no se siente realmente bien para pasar contexto, ya que esto podría ser tan grande como un párrafo largo, o incluso más. En cuanto a las sesiones, eso puede funcionar. pero realmente no se siente bien, ya que este no parece ser el propósito correcto de las sesiones ...
Tienda de Enrique Moreno
@Dbugger Vaya, no me di cuenta de que había hecho la pregunta. Si le preocupa pasar demasiada información al usuario a través de la cadena de consulta, podría ser mejor almacenar los datos de validación en una base de datos y luego consultarla cuando llegue a la /ruta principal . Otra opción es llamar a la ruta posterior a través de AJAX y almacenar los datos resultantes en almacenamiento local o algo similar.
AlbertEngelB
13
Buena respuesta, pero creo que las sesiones son el mejor enfoque que las cadenas de consulta: ¡no exponga cosas en las URL!
user2562923
2
@ user2562923 Estoy de acuerdo, las sesiones o POST son mejores que las GET para transmitir el contexto. La respuesta fue bastante vaga, así que no estaba seguro de qué tipo de información estaba pasando el autor de la pregunta.
AlbertEngelB
El nodo js tiene 2 módulos principales que generan la cadena de consulta
Fareed Alnamrouti
42

La forma más fácil que he encontrado para pasar datos entre routeHandlers para usar next()no es necesario meterse con la redirección o las sesiones. Opcionalmente, puede llamar a su en homeCtrl(req,res)lugar de next()y simplemente pasar el reqyres

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
fuente
44
Amigo, me encanta esta respuesta. Me ayudó a salir de un agujero allí, aunque realmente necesito leer más, ya que mi instinto me estaba pidiendo que pasara req y res como argumentos para next ().
JonRed
1
Muy buena y conservadora respuesta! Mucho más fácil y mucho más seguro que las otras respuestas.
Triforcey
2
Este enfoque parece ser el más limpio, pero no estoy seguro de si dejará la barra de direcciones en la ruta más reciente o si terminará en/
Danielo515
1
@ Danielo515 desafortunadamente terminará en / categories, por lo que en realidad es lo mismo que mostrar la vista directamente en / categories ...
Manuel Graf
2
No estoy de acuerdo con que solo esté usando el enrutador como un servicio para representar una vista diferente que dejará la URL como está, es una mala idea estructurar su código de esa manera, el propósito del siguiente es pasar al siguiente middleware y generalmente aislar nuestra lógica de negocios de la lógica de enrutadores
Fareed Alnamrouti
9

Tuve que encontrar otra solución porque ninguna de las soluciones proporcionadas realmente cumplió con mis requisitos, por las siguientes razones:

  • Cadenas de consulta : es posible que no desee utilizar cadenas de consulta porque los usuarios podrían compartir las URL y, a veces, los parámetros de consulta no tienen sentido para un usuario diferente. Por ejemplo, un error como ?error=sessionExpirednunca debe mostrarse a otro usuario por accidente.

  • sesión requerida : es posible que no desee usarlo req.sessionporque necesita la dependencia de sesión rápida para esto, lo que incluye la configuración de un almacén de sesión (como MongoDB), que puede que no necesite en absoluto, o tal vez ya esté utilizando una costumbre solución de tienda de sesión.

  • siguiente () : es posible que no desee utilizarlo next()o, next("router")debido a que esto esencialmente solo representa su nueva página bajo la URL original, en realidad no es una redirección a la nueva URL, más como un reenvío / reescritura, que puede no ser aceptable.

Así que esta es mi cuarta solución que no sufre ninguno de los problemas anteriores. Básicamente implica el uso de una cookie temporal, para lo cual primero deberá instalar el analizador de cookies . Obviamente, esto significa que solo funcionará cuando las cookies estén habilitadas y con una cantidad limitada de datos.

Ejemplo de implementación:

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
fuente
No se requiere ninguna tienda de sesión para express-session. Una base de datos solo hace que las sesiones persistan en los reinicios del servidor.
Mike 'Pomax' Kamermans
6

use app.set y app.get

Establecer datos

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

Obteniendo datos

router.get("/sign", (req, res) => {
  console.log('sign', req.app.get('user'))
});
UA_
fuente
2
mala idea para implementarlo. puede afectar a todos los usuarios, todos req.app es igual para todos los usuarios. podemos usar express-session en lugar de esto.
ujjal das
5

Esto es lo que sugiero sin usar ninguna otra dependencia, solo nodo y express, use app.locals, aquí hay un ejemplo:

    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
fuente
2
Siento que esta es una mala idea porque req.app.locals parece afectar a todos los usuarios, no solo al que realizó la solicitud. Seguro que borrará los datos tan pronto como pasen al usuario, pero me imagino que aún tendrá conflictos y mostrará información incorrecta de vez en cuando. Y si tiene que borrarlo de todos modos, también podría almacenarlo en la sesión.
apiladores
1
en realidad solo afectará la solicitud enviada por los usuarios "únicos", incluso si 2 o más usuarios envían solicitudes al "mismo tiempo", cada usuario tendrá su propio objeto de solicitud y su objeto local, de esa manera es completamente seguro uso
Hussein Dimessi
req es para un usuario único, pero req.app es para todos los usuarios.
ZeroCho
use express-session para enviar el mensaje
ujjal das
3

Puede pasar pequeños bits de datos de pares clave / valor a través de la cadena de consulta:

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

Y JavaScript en la página de inicio puede acceder a eso y ajustar su comportamiento en consecuencia.

Tenga en cuenta que si no le importa /categoryquedarse como la URL en la barra de direcciones del navegador, puede renderizar directamente en lugar de redirigir. En mi humilde opinión, muchas veces las personas usan redireccionamientos porque los marcos web más antiguos dificultaban la respuesta directa, pero es fácil de expresar:

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

  // Process the data received in req.body

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

Como comentó @ Dropped.on.Caprica, el uso de AJAX elimina la preocupación de cambio de URL.

Peter Lyons
fuente
Como dije en otra respuesta, las cadenas de consulta no parecen buenas, ya que aún no sé qué tan grandes son los datos que quiero pasar. Su otra solución es la más agradable que he encontrado, pero aún así rompe mi intención de mantener la misma URL.
Tienda Enrique Moreno
@Dbugger ¿Usar un POSTE AJAX posiblemente?
AlbertEngelB
1

podemos usar la sesión express para enviar los datos requeridos

cuando inicializa la aplicación

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

así que antes de la redirección solo guarde el contexto para la sesión

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

Ahora puede obtener el contexto en cualquier lugar para la sesión. se puede obtener solo 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
fuente