Làm cách nào để chuyển hướng trong expressjs trong khi vượt qua một số bối cảnh?


270

Tôi đang sử dụng express để tạo một ứng dụng web trong node.js. Đây là một sự đơn giản hóa những gì tôi có:

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

Vấn đề của tôi là như sau:

Nếu tôi thấy rằng dữ liệu được gửi /categorykhông hợp lệ, tôi muốn chuyển một số ngữ cảnh bổ sung cho /trang. Làm thế nào tôi có thể làm điều này? Redirect dường như không cho phép bất kỳ loại tham số phụ.



Thuật ngữ quan trọng ở đây: không có /trang. Có một /tuyến đường mà express có thể phục vụ và có một mẫu trang chủ, express có thể hiển thị. Nếu bạn muốn lưu giữ một số dữ liệu để hiển thị mẫu trang chủ sau khi chuyển hướng, câu trả lời được chấp nhận mặc dù, các phiên là bạn của bạn. Họ cung cấp cho bạn một kho lưu trữ dữ liệu vẫn tồn tại qua các yêu cầu và phản hồi cho từng người dùng duyệt web để bạn có thể đưa dữ liệu vào trong quá trình /categoryxử lý và sau đó lấy nó ra nếu có trong khi xử lý /.
Mike 'Pomax' Kamermans

Câu trả lời:


368

Có một vài cách truyền dữ liệu xung quanh các tuyến đường khác nhau. Tất nhiên, câu trả lời đúng nhất là các chuỗi truy vấn. Bạn sẽ cần phải đảm bảo rằng các giá trị là đúng encodeURIComponentdecodeURIComponent .

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

Bạn có thể chặn điều đó trong tuyến khác bằng cách lấy các tham số được gửi bằng cách sử dụng req.query.

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

Để có cách năng động hơn, bạn có thể sử dụng urlmô-đun lõi để tạo chuỗi truy vấn cho mình:

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

Vì vậy, nếu bạn muốn chuyển hướng tất cả các biến chuỗi truy vấn req, bạn chỉ cần làm

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

Và nếu bạn đang sử dụng Node> = 7.x, bạn cũng có thể sử dụng querystringmô-đun lõi

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

Một cách khác để làm điều đó là bằng cách thiết lập một cái gì đó trong phiên. Bạn có thể đọc cách thiết lập nó ở đây , nhưng để thiết lập và truy cập các biến là như thế này:

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

Và sau này sau khi chuyển hướng ...

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

Ngoài ra còn có tùy chọn sử dụng một tính năng cũ của Express , req.flash. Làm như vậy trong các phiên bản mới hơn của Express sẽ yêu cầu bạn sử dụng một thư viện khác. Về cơ bản, nó cho phép bạn thiết lập các biến sẽ hiển thị và đặt lại vào lần tiếp theo bạn đến một trang. Nó tiện dụng để hiển thị lỗi cho người dùng, nhưng một lần nữa, nó đã bị xóa theo mặc định. EDIT: Tìm thấy một thư viện có thêm chức năng này.

Hy vọng rằng điều đó sẽ cung cấp cho bạn một ý tưởng chung về cách truyền thông tin xung quanh trong một ứng dụng Express.


6
Các truy vấn không cảm thấy thực sự ổn để vượt qua bối cảnh, vì điều này có thể lớn như một đoạn văn dài, hoặc thậm chí nhiều hơn. Đối với các phiên, điều đó có thể làm việc. nhưng nó không thực sự cảm thấy đúng, vì điều này dường như không đúng mục đích của các phiên ...
Lều Enrique Moreno

@Dbugger Rất tiếc, không nhận ra bạn đã đặt câu hỏi. Nếu bạn lo lắng về việc truyền quá nhiều thông tin cho người dùng thông qua chuỗi truy vấn, có thể tốt hơn là lưu trữ dữ liệu xác thực trong DB và sau đó truy vấn khi bạn nhấn /tuyến chính . Một tùy chọn khác là gọi tuyến đường bưu điện qua AJAX và lưu trữ dữ liệu kết quả trong bộ lưu trữ cục bộ hoặc một cái gì đó tương tự.
AlbertEngelB

13
Câu trả lời hay nhưng tôi nghĩ phiên là cách tiếp cận tốt hơn chuỗi truy vấn - không để lộ nội dung trong URL!
dùng2562923

2
@ user2562923 Tôi đồng ý, các phiên hoặc POST tốt hơn GET để chuyển ngữ cảnh xung quanh. Câu trả lời khá mơ hồ nên tôi không chắc người hỏi đang gửi thông tin gì.
AlbertEngelB

nút js có 2 mô-đun cốt lõi tạo ra chuỗi truy vấn
Fareed Alnamrouti

42

Cách dễ nhất mà tôi đã tìm thấy để truyền dữ liệu giữa routeHandlers để next()không cần phải lộn xộn với chuyển hướng hoặc phiên. Tùy chọn bạn chỉ có thể gọi cho bạn homeCtrl(req,res)thay vì next()và chỉ cần vượt qua 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
Buddy, tôi thích câu trả lời này. Đã giúp tôi thoát khỏi một lỗ hổng ở đó - mặc dù tôi thực sự cần đọc nhiều hơn vì ruột của tôi đang khóc để tôi vượt qua req và giải quyết như là đối số cho tiếp theo ().
JonRed

1
Câu trả lời rất hay và bảo thủ! Dễ dàng hơn nhiều và an toàn hơn nhiều so với các câu trả lời khác.
Triforcey

2
Cách tiếp cận này có vẻ sạch hơn, nhưng tôi không chắc liệu nó sẽ rời khỏi thanh địa chỉ trong đường dẫn mới nhất hay nó sẽ kết thúc tại/
Danielo515

1
@ Danielo515 nó sẽ kết thúc tại / loại không may, vì vậy điều này thực sự giống như chỉ hiển thị chế độ xem trực tiếp trong / danh mục ...
Manuel Graf

2
Tôi không đồng ý rằng bạn chỉ sử dụng bộ định tuyến như một dịch vụ để hiển thị chế độ xem bị trì hoãn sẽ rời khỏi URL vì nó là ý tưởng tồi để cấu trúc mã của bạn theo cách đó, mục đích tiếp theo là chuyển sang phần mềm trung gian tiếp theo và chúng tôi thường cô lập logic kinh doanh của chúng tôi khỏi logic bộ định tuyến
Fareed Alnamrouti

9

Tôi đã phải tìm một giải pháp khác vì không có giải pháp nào được cung cấp thực sự đáp ứng yêu cầu của tôi, vì những lý do sau:

  • Chuỗi truy vấn : Bạn có thể không muốn sử dụng chuỗi truy vấn vì các URL của người dùng có thể được chia sẻ và đôi khi các tham số truy vấn không có ý nghĩa đối với người dùng khác. Ví dụ, một lỗi như ?error=sessionExpiredkhông bao giờ được hiển thị cho người dùng khác một cách tình cờ.

  • req.session : Bạn có thể không muốn sử dụngreq.session vì bạn cần phụ thuộc phiên nhanh cho việc này, bao gồm thiết lập một cửa hàng phiên (chẳng hạn như MongoDB), mà bạn có thể không cần, hoặc có thể bạn đang sử dụng một tùy chỉnh giải pháp lưu trữ phiên.

  • kế tiếp() : Bạn có thể không muốn sử dụng next()hoặc next("router")vì điều này về cơ bản chỉ hiển thị trang mới của bạn dưới URL gốc, nó không thực sự là một chuyển hướng đến URL mới, giống như chuyển tiếp / viết lại, có thể không được chấp nhận.

Vì vậy, đây là giải pháp thứ tư của tôi không gặp phải bất kỳ vấn đề nào trước đây. Về cơ bản, nó liên quan đến việc sử dụng cookie tạm thời, trước tiên bạn sẽ phải cài đặt trình phân tích cú pháp cookie . Rõ ràng điều này có nghĩa là nó sẽ chỉ hoạt động khi cookie được bật và với một lượng dữ liệu hạn chế.

Ví dụ thực hiệ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("/");
}

Không có cửa hàng phiên là cần thiết cho express-session. Một cơ sở dữ liệu chỉ làm cho các phiên liên tục trên máy chủ khởi động lại.
Mike 'Pomax' Kamermans

6

sử dụng app.set & app.get

Cài đặt dữ liệu

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

Lấy dữ liệu

router.get("/sign", (req, res) => {
  console.log('sign', req.app.get('user'))
});

2
ý tưởng tồi để thực hiện nó. nó có thể ảnh hưởng đến tất cả người dùng, tất cả req.app đều giống nhau đối với tất cả người dùng. chúng ta có thể sử dụng express-session thay vì cái này.
ujjal das

5

Đây là những gì tôi đề xuất mà không sử dụng bất kỳ sự phụ thuộc nào khác, chỉ cần nút và express, sử dụng app.locals, đây là một ví dụ:

    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
Tôi cảm thấy như đây là một ý tưởng tồi vì req.app.locals dường như ảnh hưởng đến tất cả người dùng, không chỉ người đưa ra yêu cầu. Chắc chắn bạn sẽ xóa dữ liệu ngay khi nó được chuyển đến người dùng, nhưng tôi tưởng tượng bạn vẫn có dữ liệu xung đột và thỉnh thoảng hiển thị thông tin không chính xác. Và nếu bạn phải xóa nó bằng mọi cách, bạn cũng có thể lưu trữ nó trong phiên thay thế.
máy xếp chồng

1
thực tế nó sẽ chỉ thực hiện yêu cầu được gửi bởi người dùng "duy nhất", ngay cả khi 2 hoặc nhiều người dùng gửi yêu cầu cùng lúc "mỗi người dùng sẽ có đối tượng yêu cầu riêng và đối tượng địa phương của mình, theo cách đó hoàn toàn an toàn với sử dụng
Hussein Dimessi

req dành cho một người dùng duy nhất, nhưng req.app dành cho tất cả người dùng.
ZeroCho

sử dụng phiên cấp tốc để gửi tin nhắn
ujjal das

3

Bạn có thể truyền các bit nhỏ của dữ liệu cặp khóa / giá trị thông qua chuỗi truy vấn:

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

Và javascript trên trang chủ có thể truy cập và điều chỉnh hành vi của nó cho phù hợp.

Lưu ý rằng nếu bạn không nhớ /categorylà URL trong thanh địa chỉ trình duyệt, bạn chỉ có thể kết xuất trực tiếp thay vì chuyển hướng. IMHO nhiều lần mọi người sử dụng chuyển hướng vì các khung web cũ hơn khiến việc phản hồi trực tiếp trở nên khó khăn, nhưng thật dễ dàng để thể hiện:

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

  // Process the data received in req.body

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

Như @ Droppping.on.Caprica đã nhận xét, sử dụng AJAX giúp loại bỏ mối quan tâm thay đổi URL.


Như tôi đã nói trong một câu trả lời khác, các chuỗi truy vấn có vẻ không tốt, vì tôi chưa biết dữ liệu tôi muốn vượt qua lớn đến mức nào. Giải pháp khác của bạn là tôi đã tìm thấy nhiều thông tin hơn, nhưng nó vẫn phá vỡ ý định giữ cùng một URL.
Lều Enrique Moreno

@Dbugger Có thể sử dụng AJAX POST?
AlbertEngelB

1

chúng ta có thể sử dụng phiên nhanh để gửi dữ liệu cần thiết

khi bạn khởi tạo ứng dụng

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

vì vậy trước khi chuyển hướng chỉ cần lưu bối cảnh cho phiên

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

Bây giờ bạn có thể có được bối cảnh bất cứ nơi nào cho phiên. nó có thể nhận được chỉ bằng req.session.context

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

    // So prepare the context
var context=req.session.context;
    res.render('home.jade', context);
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.