Как мне перенаправить в expressjs при передаче некоторый контекст?

270

Я использую экспресс для создания веб-приложения в node.js. Это упрощение того, что у меня есть:

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

Моя проблема заключается в следующем:

Если я обнаружу, что отправленные данные /categoryне проверяются, я хотел бы передать дополнительный контекст на /страницу. Как я мог это сделать? Redirect, кажется, не допускает каких-либо дополнительных параметров.

Энрике Морено Палатка
источник
1
Проверить экспресс » flash: stackoverflow.com/questions/12442716/…
tymeJV
Терминология имеет значение здесь: нет /страницы. Существует /маршрут, который экспресс может обслуживать, и есть шаблон домашней страницы, который экспресс может отображать. Если вы хотите сохранить некоторые данные для рендеринга шаблона домашней страницы после перенаправления, несмотря на принятый ответ, сеансы - ваш друг. Они предоставляют вам хранилище данных, которое сохраняется между запросами и ответами для отдельных пользователей, просматривающих данные, так что вы можете поместить данные во время /categoryобработки, а затем извлечь их, если они есть во время /передачи.
Майк 'Pomax' Камерманс

Ответы:

368

Есть несколько способов передачи данных по разным маршрутам. Самый правильный ответ - это, конечно, строки запроса. Вы должны убедиться , что значения правильно encodeURIComponent и decodeURIComponent .

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

Вы можете поймать это в вашем другом маршруте, получая параметры, отправленные с помощью req.query.

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

Для более динамичного способа вы можете использовать urlосновной модуль для генерации строки запроса для вас:

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

Поэтому, если вы хотите перенаправить все переменные строки запроса req, вы можете просто сделать

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

И если вы используете Node> = 7.x, вы также можете использовать querystringосновной модуль

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

Другой способ сделать это - настроить что-то в сеансе. Вы можете прочитать, как установить его здесь , но установить и получить доступ к переменным это примерно так:

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

А потом после перенаправления ...

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

Существует также возможность использования старой функции Express req.flash. Для этого в более новых версиях Express вам потребуется использовать другую библиотеку. По сути, это позволяет вам устанавливать переменные, которые будут отображаться и сбрасываться при следующем входе на страницу. Он удобен для отображения ошибок пользователям, но опять же он был удален по умолчанию. РЕДАКТИРОВАТЬ: Найдена библиотека, которая добавляет эту функциональность.

Надеюсь, это даст вам общее представление о том, как передавать информацию в приложении Express.

AlbertEngelB
источник
6
Querys не чувствуют себя действительно хорошо, чтобы передать контекст, так как это может быть большим, как длинный абзац, или даже больше. Что касается сессий, это может работать. но на самом деле это не так, потому что это кажется неправильной целью сессий ...
Энрике Морено Палатка
@Dbugger Ой, не понял, что вы задали вопрос. Если вас беспокоит передача слишком большого количества информации пользователю через строку запроса, может быть лучше сохранить данные проверки в БД, а затем запросить их при попадании на основной /маршрут. Другой вариант - вызвать почтовый маршрут через AJAX и сохранить полученные данные в локальном хранилище или в чем-то подобном.
AlbertEngelB
13
Хороший ответ, но я думаю, что сессии - лучший подход, чем строки запроса - не показывайте вещи в URL!
user2562923
2
@ user2562923 Я согласен, сеансы или POST лучше, чем GET для передачи контекста. Ответ был довольно расплывчатым, поэтому я не был уверен, какую информацию раздает этот спрашивающий.
AlbertEngelB
узел js имеет 2 основных модуля, которые генерируют строку запроса
Фарид Алнамрути
42

Я нашел самый простой способ передачи данных между routeHandlers, чтобы next()не было необходимости связываться с редиректом или сессиями. При желании вы могли бы просто назвать свою homeCtrl(req,res)вместо next()и просто пройти reqиres

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
источник
4
Приятель, я люблю этот ответ. Это помогло мне выйти из дыры - хотя мне действительно нужно больше читать, потому что моя интуиция просила меня передать req и res в качестве аргументов для next ().
JonRed
1
Очень хороший и консервативный ответ! Гораздо проще и безопаснее, чем другие ответы.
Triforcey
2
Этот подход кажется более чистым, но я не уверен, покинет ли он адресную строку по последнему пути или закончится ли он/
Danielo515
1
@ Danielo515 это, к сожалению, закончится в / категориях, так что на самом деле это то же самое, что просто отобразить представление непосредственно в / категориях ...
Мануэль Граф
2
Я не согласен с тем, что вы просто используете маршрутизатор в качестве службы для представления двухстороннего представления, которое оставит URL-адрес как есть, плохая идея структурировать свой код таким образом, цель следующего - перейти к следующему промежуточному программному обеспечению, и мы обычно изолировать нашу бизнес-логику от логики маршрутизаторов
Фарид Алнамрути
9

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

  • Строки запроса . Возможно, вы не захотите использовать строки запроса, поскольку URL-адреса могут совместно использоваться вашими пользователями, а иногда параметры запроса не имеют смысла для другого пользователя. Например, ошибка, такая как ?error=sessionExpiredникогда не должна отображаться другому пользователю случайно.

  • req.session : вы можете не использовать, req.sessionпотому что для этого вам нужна зависимость экспресс-сессии , которая включает в себя настройку хранилища сеансов (например, MongoDB), которое вам может вообще не понадобиться, или, возможно, вы уже используете пользовательский решение для магазина сессий.

  • next () : вы, возможно, не захотите использовать next()или next("router")потому, что по сути это просто отображает вашу новую страницу под исходным URL-адресом, на самом деле это не перенаправление на новый URL-адрес, а скорее переадресация / перезапись, что может быть неприемлемо.

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

Пример реализации:

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
источник
Нет хранилища сессий не требуется для express-session. База данных просто сохраняет сеансы при перезапусках сервера.
Майк 'Pomax' Камерманс
6

использовать app.set & app.get

Настройка данных

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

Получение данных

router.get("/sign", (req, res) => {
  console.log('sign', req.app.get('user'))
});
UA_
источник
2
плохая идея реализовать это. это может повлиять на всех пользователей, все req.app одинаково для всех пользователей. мы можем использовать экспресс-сессию вместо этого.
Уджал дас
5

Вот то, что я предлагаю без использования какой-либо другой зависимости, просто узел и экспресс, используйте app.locals, вот пример:

    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("/");
    }
Хуссейн Димесси
источник
2
Я чувствую, что это плохая идея, потому что req.app.locals, похоже, влияет на всех пользователей, а не только на того, кто сделал запрос. Конечно, вы очистите данные, как только они будут переданы пользователю, но я думаю, что вы все равно будете конфликтовать и время от времени показывать неверную информацию. И если вам все равно придется очистить его, вы можете просто сохранить его в сеансе.
укладчики
1
на самом деле это будет влиять только на запрос, отправленный «уникальными» пользователями, даже если 2 или более пользователей отправляют запросы «в одно и то же время», каждый пользователь будет иметь свой собственный объект запроса и свой объект locals, таким образом, это абсолютно безопасно для пользователя. использование
Хуссейн Димесси
req для уникального пользователя, но req.app для всех пользователей.
ZeroCho
используйте экспресс-сессию для отправки сообщения
Уджал дас
3

Вы можете передать небольшие биты данных пары ключ / значение через строку запроса:

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

И javascript на домашней странице может получить к нему доступ и соответствующим образом изменить свое поведение.

Обратите внимание, что если вы не возражаете против /categoryсохранения URL-адреса в адресной строке браузера, вы можете просто выполнить рендеринг напрямую, а не перенаправлять. ИМХО, много раз люди используют перенаправления, потому что старые веб-фреймворки затрудняют прямой ответ, но в экспрессе это легко:

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

  // Process the data received in req.body

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

Как прокомментировал @ Dropped.on.Caprica, использование AJAX устраняет проблему изменения URL.

Питер Лайонс
источник
Как я сказал в другом ответе, строки запроса не кажутся хорошими, так как я еще не знаю, насколько велики данные, которые я хочу передать. Ваше другое решение - более подходящее, которое я нашел, но оно все еще ломает мое намерение сохранить тот же URL.
Энрике Морено Палатка
@Dbugger Использовать AJAX POST возможно?
AlbertEngelB
1

мы можем использовать экспресс-сессию для отправки необходимых данных

когда вы инициализируете приложение

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

поэтому перед перенаправлением просто сохраните контекст для сеанса

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

Теперь вы можете получить контекст где угодно для сеанса. это можно получить только путем req.session.context

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

    // So prepare the context
var context=req.session.context;
    res.render('home.jade', context);
});
Уджал дас
источник