ฉันจะเปลี่ยนเส้นทางใน expressjs ขณะผ่านบริบทได้อย่างไร


269

ฉันใช้ express เพื่อสร้างเว็บแอปใน 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ไม่ได้ตรวจสอบความถูกต้องฉันต้องการส่งบริบทเพิ่มเติมไปยัง/หน้า ฉันจะทำสิ่งนี้ได้อย่างไร การเปลี่ยนเส้นทางดูเหมือนจะไม่อนุญาตให้มีพารามิเตอร์พิเศษใด ๆ


1
ตรวจสอบด่วน ' flash: stackoverflow.com/questions/12442716/ …
tymeJV

คำศัพท์มีความสำคัญที่นี่: ไม่มี/หน้า มี/เส้นทางซึ่ง express สามารถให้บริการได้และมีเทมเพลตโฮมเพจซึ่ง express สามารถแสดงผลได้ หากคุณต้องการเก็บข้อมูลบางอย่างไว้สำหรับการแสดงเทมเพลตหน้าแรกหลังจากเปลี่ยนเส้นทางคำตอบที่ยอมรับแม้ว่าเซสชันจะเป็นเพื่อนของคุณ พวกเขาให้คุณเป็นแหล่งข้อมูลที่ยังคงมีอยู่ในคำขอและการตอบสนองสำหรับผู้ใช้งานแต่ละคนเพื่อให้คุณสามารถใส่ข้อมูลในระหว่างการ/categoryจัดการแล้วนำออกมาถ้ามันมีในระหว่างการ/ส่ง
ไมค์ 'Pomax' Kamermans

คำตอบ:


367

มีสองสามวิธีในการส่งข้อมูลไปยังเส้นทางที่แตกต่างกัน แน่นอนคำตอบที่ถูกต้องที่สุดคือสตริงคำค้นหา คุณจะต้องตรวจสอบให้แน่ใจว่าค่านั้นถูกเข้ารหัสอย่างสมบูรณ์URICซึ่งเป็นส่วนประกอบและตัวถอดรหัสนั้นจะขึ้นอยู่กับส่วนประกอบ

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

req.flashนอกจากนี้ยังมีตัวเลือกของการใช้คุณลักษณะเก่าเอ็กซ์เพรส, การทำเช่นนี้กับ Express เวอร์ชันใหม่จะทำให้คุณต้องใช้ไลบรารีอื่น เป็นหลักจะช่วยให้คุณสามารถตั้งค่าตัวแปรที่จะปรากฏขึ้นและตั้งค่าในครั้งต่อไปที่คุณไปที่หน้า มีประโยชน์ในการแสดงข้อผิดพลาดให้กับผู้ใช้ แต่จะถูกลบโดยค่าเริ่มต้นอีกครั้ง แก้ไข: พบห้องสมุดที่เพิ่มฟังก์ชันนี้

หวังว่าจะช่วยให้คุณมีความคิดทั่วไปเกี่ยวกับวิธีส่งผ่านข้อมูลในแอปพลิเคชัน Express


6
ข้อความค้นหาไม่รู้สึกดีจริงๆที่จะผ่านบริบทเนื่องจากอาจมีขนาดใหญ่เท่ากับย่อหน้ายาวหรือมากกว่านั้น สำหรับเซสชันที่อาจใช้งานได้ แต่มันไม่ได้รู้สึกถูกต้องเพราะนี่ดูเหมือนจะไม่ใช่จุดประสงค์ที่ถูกต้องของการประชุม ...
Enrique Moreno Tent

@Dugugger โอ๊ะไม่รู้ว่าคุณเคยถามคำถาม หากคุณกังวลเกี่ยวกับการส่งข้อมูลมากเกินไปให้กับผู้ใช้ผ่านการสอบถามอาจเป็นการดีกว่าที่จะเก็บข้อมูลการตรวจสอบความถูกต้องไว้ในฐานข้อมูลจากนั้นสอบถามว่าเมื่อคุณเข้าสู่/เส้นทางหลัก อีกทางเลือกหนึ่งคือโทรเส้นทางโพสต์ผ่าน AJAX และเก็บข้อมูลที่เป็นผลลัพธ์ใน localStorage หรือสิ่งที่คล้ายกัน
AlbertEngelB

13
คำตอบที่ดี แต่ฉันคิดว่าการประชุมเป็นวิธีที่ดีกว่าสตริงการสืบค้นอย่าเปิดเผยเนื้อหาใน URL!
user2562923

2
@ user2562923 ฉันยอมรับเซสชันหรือ POST ดีกว่า GETs สำหรับการผ่านบริบท คำตอบนั้นค่อนข้างคลุมเครือดังนั้นฉันจึงไม่แน่ใจว่าข้อมูลที่ผู้ถามส่งไปนั้นเป็นอย่างไร
AlbertEngelB

โหนด js มี 2 โมดูลหลักที่สร้างสตริงแบบสอบถาม
Fareed Alnamrouti

42

วิธีที่ง่ายที่สุดที่ฉันพบว่าส่งผ่านข้อมูลระหว่างเส้นทางตัวจัดการเพื่อใช้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);

4
บัดดี้ฉันรักคำตอบนี้ ช่วยฉันออกจากรูที่นั่น - ถึงแม้ว่าฉันจะต้องอ่านหนังสือมากขึ้นเพราะความอุตสาหะของฉันกำลังร้องไห้ออกมาเพื่อให้ฉันผ่านการตอบคำถาม
JonRed

1
คำตอบที่ดีมากและอนุรักษ์นิยม! ง่ายกว่าและปลอดภัยกว่าคำตอบอื่น ๆ
Triforcey

2
วิธีการนี้น่าจะเป็นเครื่องดูด แต่ผมไม่แน่ใจว่ามันจะได้ใส่แถบที่อยู่ในเส้นทางใหม่ล่าสุดหรือถ้ามันจะจบที่/
Danielo515

1
@ Danielo515 มันจะจบที่ / หมวดหมู่โชคไม่ดีดังนั้นนี่จึงเหมือนกับการแสดงมุมมองใน / หมวดหมู่โดยตรง ...
Manuel Graf

2
ฉันไม่เห็นด้วยที่คุณเพียงแค่ใช้เราเตอร์เป็นบริการในการแสดงผลแบบเลื่อนซึ่งจะออกจาก URL เพราะมันเป็นความคิดที่ไม่ดีที่จะจัดโครงสร้างโค้ดของคุณในแบบนั้นจุดประสงค์ของการถัดไปคือ แยกตรรกะทางธุรกิจของเราออกจากตรรกะเราเตอร์
Fareed Alnamrouti

9

ฉันต้องหาวิธีแก้ปัญหาอื่นเพราะไม่มีวิธีใดที่ตรงตามความต้องการของฉันด้วยเหตุผลดังต่อไปนี้:

  • สตริงข้อความค้นหา : คุณอาจไม่ต้องการใช้สตริงข้อความค้นหาเนื่องจากผู้ใช้ของคุณสามารถใช้ URL ร่วมกันได้และบางครั้งพารามิเตอร์ข้อความค้นหาจะไม่สมเหตุสมผลสำหรับผู้ใช้รายอื่น ตัวอย่างเช่นข้อผิดพลาดเช่น?error=sessionExpiredไม่ควรปรากฏต่อผู้ใช้รายอื่นโดยไม่ได้ตั้งใจ

  • req.session : คุณอาจไม่ต้องการใช้req.sessionเพราะคุณต้องการการพึ่งพาเซสชันด่วนสำหรับสิ่งนี้ซึ่งรวมถึงการตั้งค่าที่เก็บเซสชัน (เช่น MongoDB) ซึ่งคุณอาจไม่ต้องการเลยหรือคุณอาจจะใช้แบบกำหนดเองอยู่แล้ว โซลูชันการจัดเก็บเซสชัน

  • ถัดไป () : คุณอาจไม่ต้องการใช้next()หรือnext("router")เพราะนี่เป็นเพียงการแสดงผลหน้าใหม่ของคุณภายใต้ URL ดั้งเดิมมันไม่ได้เปลี่ยนเส้นทางไปยัง URL ใหม่เช่นเดียวกับการส่งต่อ / เขียนใหม่ซึ่งอาจไม่เป็นที่ยอมรับ

นี่คือทางออกที่สี่ของฉันที่ไม่ได้รับผลกระทบจากปัญหาก่อนหน้าใด ๆ โดยทั่วไปจะเกี่ยวข้องกับการใช้คุกกี้ชั่วคราวซึ่งคุณจะต้องติดตั้งcookie-parserก่อน เห็นได้ชัดว่านี่หมายความว่ามันจะทำงานเฉพาะที่เปิดใช้งานคุกกี้และมีข้อมูลจำนวน จำกัด

ตัวอย่างการนำไปใช้:

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ไม่มีร้านเซสชั่นเป็นสิ่งจำเป็นสำหรับ ฐานข้อมูลทำให้เซสชันยังคงอยู่ระหว่างการรีสตาร์ทเซิร์ฟเวอร์
Kamermans ของไมค์ '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'))
});

2
ความคิดที่ไม่ดีที่จะใช้มัน มันสามารถส่งผลกระทบต่อผู้ใช้ทั้งหมด req.app ทั้งหมดเหมือนกันสำหรับผู้ใช้ทั้งหมด เราสามารถใช้เซสชันด่วนแทนสิ่งนี้
ujjal das

5

นี่คือสิ่งที่ฉันแนะนำโดยไม่ใช้การพึ่งพาใด ๆ เพียงแค่โหนดและ 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 ดูเหมือนว่าจะส่งผลกระทบต่อผู้ใช้ทุกคนไม่ใช่แค่คนที่ทำคำขอ แน่นอนว่าคุณจะล้างข้อมูลทันทีที่ส่งผ่านไปยังผู้ใช้ แต่ฉันคิดว่าคุณยังคงมีข้อมูลที่ขัดแย้งกันและแสดงข้อมูลที่ไม่ถูกต้องเป็นครั้งคราว และถ้าคุณต้องล้างมันคุณอาจจะเก็บมันไว้ในเซสชั่นแทนก็ได้เช่นกัน
stackers

1
จริง ๆ แล้วมันจะมีผลต่อคำขอที่ส่งโดยผู้ใช้ "ที่ไม่ซ้ำกัน" เท่านั้นแม้ว่าผู้ใช้ 2 คนขึ้นไปส่งคำขอที่ "เวลาเดียวกัน" ผู้ใช้แต่ละคนจะมีวัตถุคำขอของตัวเองและวัตถุในท้องถิ่นของตน ใช้
Hussein Dimessi

req สำหรับผู้ใช้ที่ไม่ซ้ำกัน แต่ req.app สำหรับผู้ใช้ทั้งหมด
ZeroCho

ใช้เซสชันด่วนเพื่อส่งข้อความ
ujjal das

3

คุณสามารถส่งบิตคู่ของข้อมูลคู่ของคีย์ / ค่าผ่านสตริงการสืบค้น:

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

และจาวาสคริปต์ในหน้าแรกสามารถเข้าถึงและปรับพฤติกรรมให้เหมาะสม

โปรดทราบว่าหากคุณไม่คิด/categoryว่าจะเป็น URL ในแถบที่อยู่ของเบราว์เซอร์คุณสามารถแสดงผลได้โดยตรงแทนที่จะเปลี่ยนเส้นทาง หลายครั้งที่ผู้คนใช้การเปลี่ยนเส้นทาง IMHO เพราะเฟรมเวิร์กเว็บที่เก่ากว่าทำให้การตอบสนองโดยตรงยาก แต่ก็แสดงได้ง่าย:

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 เดียวกันเอาไว้
Enrique Moreno Tent

@Dugugger ใช้ 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);
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.