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

Posted

技术标签:

【中文标题】如何在传递一些上下文时在 expressjs 中重定向?【英文标题】:How do I redirect in expressjs while passing some context? 【发布时间】:2013-10-02 20:13:30 【问题描述】:

我正在使用 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 中发送的数据没有通过验证,我想将一些额外的上下文传递给/ 页面。我怎么能这样做?重定向似乎不允许任何类型的额外参数。

【问题讨论】:

退房快递'flash:***.com/questions/12442716/… 术语在这里很重要:没有/ 页面。有/路由,express可以服务,还有首页模板,express可以渲染。如果您想保留一些数据以在重定向后呈现主页模板,尽管接受的答案是,会话是您的朋友。它们为您提供了一个跨请求和响应持续存在的数据存储供个人浏览用户使用,因此您可以在处理/category 期间将数据放入,然后在处理/ 期间将其取出。 【参考方案1】:

有几种方法可以将数据传递到不同的路由。最正确的答案当然是查询字符串。您需要确保值正确为 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);
 );

另一种方法是在会话中进行设置。 You can read how to set it up here,但是设置和访问变量是这样的:

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 中这样做需要您使用另一个库。本质上,它允许您设置将在您下次访问页面时显示和重置的变量。它可以方便地向用户显示错误,但默认情况下它已被删除。编辑:Found a library that adds this functionality.

希望这能让您大致了解如何在 Express 应用程序中传递信息。

【讨论】:

查询在传递上下文时感觉不太好,因为这可能与长段落一样大,甚至更多。至于会话,这可能有效。但感觉不太对劲,因为这似乎不是会话的正确目的...... @Dbugger 哎呀,不知道你问了这个问题。如果您担心通过查询字符串向用户传递太多信息,最好将验证数据存储在数据库中,然后在您点击主要的/ 路由时进行查询。另一种选择是通过 AJAX 调用 post 路由并将结果数据存储在 localstorage 或类似的东西中。 很好的答案,但我认为会话是比查询字符串更好的方法 - 不要在 URL 中公开内容! @user2562923 我同意,会话或 POST 在传递上下文方面比 GET 更好。答案很模糊,所以我不确定提问者传递了什么样的信息。 node js 有2个核心模块生成查询字符串【参考方案2】:

我发现在 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);

【讨论】:

伙计,我喜欢这个答案。帮助我摆脱了那里的一个洞——尽管我真的需要做更多的阅读,因为我的直觉要求我将 req 和 res 作为 next() 的参数传递。 非常好的和保守的答案!比其他答案更容易、更安全。 这种方法似乎更干净,但我不确定它是否会将地址栏留在最新路径中,或者是否会以/结尾 @Danielo515 不幸的是它将在 /categories 结束,所以这实际上与直接在 /categories 中渲染视图相同...... 我不同意你只是使用路由器作为服务来呈现不同的视图,这将使 URL 保持原样,这样构造你的代码是个坏主意,下一步的目的是移动到下一个中​​间件,我们通常将业务逻辑与路由器逻辑隔离【参考方案3】:

我不得不寻找另一个解决方案,因为所提供的解决方案都没有真正满足我的要求,原因如下:

查询字符串:您可能不想使用查询字符串,因为您的用户可能会共享这些 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 不需要会话存储。数据库只是使会话在服务器重新启动时保持不变。【参考方案4】:

使用 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'))
);

【讨论】:

实施它的坏主意。它会影响所有用户所有 req.app 对所有用户都是相同的。我们可以使用 express-session 代替这个。 或设置用户为 userId req.app.set('userId', res.req.user)【参考方案5】:

我们可以使用 express-session 发送所需的数据

当你初始化应用时

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

【讨论】:

【参考方案6】:

这是我的建议,不使用任何其他依赖项,只需 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("/");
    

【讨论】:

我觉得这是个坏主意,因为 req.app.locals 似乎会影响所有用户,而不仅仅是发出请求的用户。当然,您会在将数据传递给用户后立即清除数据,但我想您仍然会遇到冲突并不时显示不正确的信息。如果您无论如何都必须清除它,您不妨将其存储在会话中。 实际上它只会影响“唯一”用户发送的请求,即使2个或更多用户在“同一时间”发送请求,每个用户都会有自己的请求对象及其本地对象, 这样使用就完全安全了 req 适用于唯一用户,但 req.app 适用于所有用户。 使用express-session发送消息【参考方案7】:

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

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 吗?【参考方案8】:

2021 年更新: 我尝试了url.formatquerystring,它们都已弃用,我们可以使用URLSearchParams

const URLSearchParams = require('url')
               
app.get('/category', (req, res) =>
    const pathname = '/?'
    const components =
    a:"a",
    b:"b"


    const urlParameters = new URLSearchParams(components)
    res.redirect(pathname + urlParameters)
)

【讨论】:

【参考方案9】:

我使用一种非常简单但有效的技术 在我的 app.js(我的入口点) 我定义了一个变量,如

let authUser = ;

然后我从我的路由页面分配给它(比如成功登录后)

authUser = matchedUser

这可能不是最好的方法,但它符合我的需要。

【讨论】:

【参考方案10】:
 app.get('/category', function(req, res) 
  var string = query
  res.redirect('/?valid=' + string);
);

在ejs中可以直接使用valid:

<% var k = valid %>

【讨论】:

另外需要注意的是,这一行,'''app.get('/', function(req, res) //准备上下文 res.render('home.jade', context ); );``` 应​​该放在最后,

以上是关于如何在传递一些上下文时在 expressjs 中重定向?的主要内容,如果未能解决你的问题,请参考以下文章

如何在带有上下文的 Django 中重定向?

ExpressJS 3.0 如何将 res.locals 传递给玉视图?

如何在 ExpressJS 和 multer 中将数据从一个路由器传递到另一个路由器?

传递 AJAX 参数并从应用程序的 expressjs 端访问它

带有 NuxtJS 中间件的 ExpressJS 将发布数据传递到页面

在 Next.js 中,如何在 axios 拦截器中重定向页面?