在传递一些上下文时如何在expressjs中重定向?


269

我正在使用express在node.js中制作一个Web应用程序。这是我所拥有的简化:

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无效,我想向/页面传递一些其他上下文。我该怎么办?重定向似乎不允许任何额外的参数。



这里的术语很重要:没有/页面。有一条/路线可以提供快递服务,也有一个主页模板可以提供快递服务。如果您想保留一些数据以在重定向后呈现主页模板,尽管可以接受,但会话还是您的朋友。它们为您提供了一个数据存储,该存储在各个浏览用户的请求和响应之间持久存在,因此您可以在/category处理过程中放入数据,然后在处理过程中将其取出/
Mike'Pomax'Kamermans

Answers:


366

有几种方法可以将数据传递到不同的路由。当然,最正确的答案是查询字符串。您需要确保值正确地是encodeURIComponentencodeURIComponent

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

因此,如果您想重定向所有请求查询字符串变量,则只需执行

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应用程序中传递信息有一个大致的了解。


6
查询并不真的可以传递上下文,因为它可能与一个长段一样大,甚至更多。至于会议,那可能行得通。但这感觉并不对,因为这似乎不是会议的正确目的……
Enrique Moreno Tent

@Dbugger糟糕,没有意识到您已经提出了这个问题。如果您担心要通过查询字符串将太多信息传递给用户,则最好将验证数据存储在数据库中,然后在击中主/路径时进行查询。另一种选择是通过AJAX调用发布路线并将结果数据存储在localstorage或类似的文件中。
AlbertEngelB 2013年

13
好的答案,但我认为会话是比查询字符串更好的方法-不要公开URL中的内容!
user2562923 2014年

2
@ user2562923我同意,会话或POST在传递上下文方面比GET更好。答案很模糊,所以我不确定询问者正在传递什么样的信息。
AlbertEngelB 2014年

节点js具有2个生成查询字符串的核心模块
Fareed Alnamrouti

42

我发现最简单的方法是在routeHandlers之间传递数据,而无需使用next()重定向或会话。(可选)您可以致电homeCtrl(req,res)而不是,next()只需传递reqres

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

4
好友,我喜欢这个答案。帮助我走出了困境-尽管我确实需要做更多的阅读,因为我的内心渴望让我传递req和res作为next()的参数。
JonRed 2014年

1
很好,很保守的答案!比其他答案更容易,更安全。
Triforcey

2
这种方法似乎更清洁,但是我不确定它是否将地址栏留在最新路径中,或者是否会在/
Danielo515 '16

1
@ Danielo515不幸的是它将在/ categories结束,因此,这实际上与直接在/ categories中呈现视图相同...
Manuel Graf

2
我不同意您只是将路由器用作服务来呈现不同的视图,该视图将保留URL,因此以这种方式构造代码是个坏主意,下一步的目的是转移到下一个中​​间件,我们通常将我们的业务逻辑与路由器逻辑隔离
Fareed Alnamrouti

9

由于以下原因,所提供的解决方案均未真正满足我的要求,因此我不得不找到另一个解决方案:

  • 查询字符串:您可能不想使用查询字符串,因为URL可以由您的用户共享,并且有时查询参数对其他用户没有意义。例如,诸如之类的错误?error=sessionExpired绝对不要偶然显示给其他用户。

  • req.session:您可能不想使用它,req.session因为您需要使用Express-Session依赖关系,包括建立一个会话存储(例如MongoDB),而这可能根本不需要,或者您已经在使用自定义会话存储解决方案。

  • next():您可能不想使用,next()或者next("router")因为这实际上只是在原始URL下呈现新页面,所以它实际上并不是重定向到新URL,更像是转发/重写,这可能是不可接受的。

因此,这是我的第四个解决方案,没有任何前述问题。基本上,它涉及使用临时Cookie,您必须首先安装它来安装cookie-parser。显然,这意味着它仅在启用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("/");
}

不需要会话存储express-session。数据库只会使会话在服务器重新启动后仍然存在。
Mike'Pomax'Kamermans,

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

2
实施它的好主意。它会影响所有用户,所有req.app对于所有用户都是相同的。我们可以使用express-session代替它。
ujjal das

5

这是我建议不使用任何其他依赖项的建议,仅使用node和express,请使用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个或更多用户在“相同时间”发送请求,每个用户也将拥有自己的请求对象及其本地对象,这样对于使用
侯赛因·迪梅西

req适用于唯一用户,但req.app适用于所有用户。
ZeroCho

使用express-session发送消息
ujjal das

3

您可以通过查询字符串传递少量的键/值对数据:

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

并且主页上的javascript可以访问它并相应地调整其行为。

请注意,如果您不介意/category停留在浏览器地址栏中的URL,则可以直接呈现而不是重定向。恕我直言,许多人使用重定向是因为较旧的Web框架使直接响应变得困难,但是表达起来很容易:

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 2013年

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);
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.