Bagaimana cara mengarahkan ulang di expressjs saat melewati beberapa konteks?


270

Saya menggunakan express untuk membuat aplikasi web di node.js. Ini adalah penyederhanaan dari apa yang saya miliki:

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

Masalah saya adalah sebagai berikut:

Jika saya menemukan bahwa data yang dikirim /categorytidak valid, saya ingin meneruskan beberapa konteks tambahan ke /halaman. Bagaimana saya bisa melakukan ini? Pengalihan tampaknya tidak mengizinkan segala jenis parameter tambahan.


1

Terminologi penting di sini: tidak ada /halaman. Ada /rute, yang ekspres bisa diservis, dan ada templat beranda, yang ekspres dapat dirender. Jika Anda ingin menyimpan beberapa data untuk merender templat beranda setelah diredirect, jawaban yang diterima sekalipun, sesi adalah teman Anda. Mereka memberi Anda penyimpanan data yang bertahan di seluruh permintaan dan tanggapan untuk pengguna penelusuran individual sehingga Anda dapat memasukkan data selama /categorypenanganan, dan kemudian membawanya keluar jika ada di sana saat /menyerahkan.
Mike 'Pomax' Kamermans

Jawaban:


368

Ada beberapa cara untuk melewatkan data ke berbagai rute. Jawaban yang paling benar tentu saja adalah string kueri. Anda harus memastikan bahwa nilainya benar encodeURIComponent dan decodeURIComponent .

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

Anda dapat mengambil hal itu di rute Anda yang lain dengan mendapatkan parameter yang dikirim menggunakan req.query.

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

Untuk cara yang lebih dinamis, Anda dapat menggunakan url modul inti untuk menghasilkan string kueri untuk Anda:

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

Jadi, jika Anda ingin mengarahkan semua variabel string permintaan req, Anda bisa melakukannya

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

Dan jika Anda menggunakan Node> = 7.x Anda juga dapat menggunakan querystring modul inti

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

Cara lain untuk melakukannya adalah dengan mengatur sesuatu di sesi. Anda dapat membaca cara mengaturnya di sini , tetapi untuk mengatur dan mengakses variabel adalah seperti ini:

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

Dan kemudian setelah pengalihan ...

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

Ada juga opsi menggunakan fitur Express yang lama, req.flash ,. Jika melakukannya dalam versi Express yang lebih baru, Anda harus menggunakan pustaka lain. Pada dasarnya itu memungkinkan Anda untuk mengatur variabel yang akan muncul dan mengatur ulang saat Anda pergi ke halaman berikutnya. Berguna untuk menunjukkan kesalahan kepada pengguna, tetapi sekali lagi itu dihapus secara default. EDIT: Menemukan perpustakaan yang menambahkan fungsi ini.

Mudah-mudahan itu akan memberi Anda gambaran umum bagaimana menyampaikan informasi di dalam aplikasi Express.


6
Querys tidak merasa benar-benar baik-baik saja untuk melewati konteks, karena ini bisa sebesar paragraf panjang, atau bahkan lebih. Adapun sesi, itu mungkin berhasil. tapi itu tidak terasa benar, karena ini sepertinya bukan tujuan yang tepat dari sesi ...
Enrique Moreno Tent

@ Debugger Ups, tidak sadar Anda telah mengajukan pertanyaan. Jika Anda khawatir tentang menyampaikan terlalu banyak informasi kepada pengguna melalui querystring, mungkin lebih baik menyimpan data validasi dalam DB dan kemudian menanyakannya ketika Anda menekan /rute utama . Pilihan lain adalah memanggil rute posting melalui AJAX dan menyimpan data yang dihasilkan di penyimpanan lokal atau yang serupa.
AlbertEngelB

13
Jawaban bagus tapi saya pikir sesi adalah pendekatan yang lebih baik daripada string kueri - jangan ungkapkan hal-hal di URL!
user2562923

2
@ user2562923 Saya setuju, sesi atau POS lebih baik daripada GET untuk melewati konteks sekitar. Jawabannya agak kabur sehingga saya tidak yakin info macam apa yang ditanyakan si penanya.
AlbertEngelB

simpul js memiliki 2 modul inti yang menghasilkan string kueri
Fareed Alnamrouti

42

Cara termudah yang saya temukan untuk meneruskan data antara routeHandlers untuk digunakan next()tidak perlu dipusingkan dengan pengalihan atau sesi. Secara opsional, Anda hanya dapat memanggil panggilan homeCtrl(req,res)alih-alih next()dan hanya meneruskan reqdanres

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
Sobat, aku suka jawaban ini. Membantu saya keluar dari lubang di sana - meskipun saya benar-benar perlu melakukan lebih banyak membaca karena usus saya berteriak agar saya lulus req dan res sebagai argumen untuk next ().
JonRed

1
Jawaban yang sangat bagus dan konservatif! Jauh lebih mudah dan jauh lebih aman daripada jawaban lainnya.
Triforcey

2
Pendekatan ini tampaknya menjadi pembersih, tetapi saya tidak yakin apakah itu akan meninggalkan bilah alamat di jalur terbaru atau jika akan berakhir pada/
Danielo515

1
@ Danielo515 itu akan berakhir pada / kategori sayangnya, jadi ini sebenarnya sama dengan hanya memberikan tampilan langsung di / kategori ...
Manuel Graf

2
Saya tidak setuju Anda hanya menggunakan router sebagai layanan untuk membuat tampilan deferent yang akan meninggalkan URL seperti itu, itu ide buruk untuk struktur kode Anda seperti itu, tujuan selanjutnya adalah pindah ke middleware berikutnya dan kami biasanya mengisolasi logika bisnis kami dari logika router
Fareed Alnamrouti

9

Saya harus menemukan solusi lain karena tidak ada solusi yang disediakan yang benar-benar memenuhi persyaratan saya, karena alasan berikut:

  • String kueri : Anda mungkin tidak ingin menggunakan string kueri karena URL dapat dibagikan oleh pengguna Anda, dan terkadang parameter kueri tidak masuk akal untuk pengguna yang berbeda. Misalnya, kesalahan seperti ?error=sessionExpiredseharusnya tidak pernah ditampilkan ke pengguna lain secara tidak sengaja.

  • req.session : Anda mungkin tidak ingin menggunakan req.sessionkarena Anda memerlukan dependensi sesi ekspres untuk ini, yang meliputi menyiapkan toko sesi (seperti MongoDB), yang mungkin tidak Anda perlukan sama sekali, atau mungkin Anda sudah menggunakan kustom solusi sesi toko.

  • next () : Anda mungkin tidak ingin menggunakan next()atau next("router")karena ini pada dasarnya hanya membuat halaman baru Anda di bawah URL asli, itu sebenarnya bukan pengalihan ke URL baru, lebih seperti pengalihan / penulisan ulang, yang mungkin tidak dapat diterima.

Jadi ini solusi keempat saya yang tidak mengalami masalah sebelumnya. Pada dasarnya ini melibatkan penggunaan cookie sementara, di mana Anda harus menginstal cookie-parser terlebih dahulu . Jelas ini berarti ini hanya akan berfungsi jika cookie diaktifkan, dan dengan jumlah data yang terbatas.

Contoh implementasi:

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

Toko sesi tidak diperlukan untuk express-session. Database hanya membuat sesi tetap di seluruh server restart.
Mike 'Pomax' Kamermans

6

gunakan app.set & app.get

Pengaturan data

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

Mendapatkan data

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

2
ide buruk untuk mengimplementasikannya. itu dapat mempengaruhi semua pengguna semua req.app sama untuk semua pengguna. kita bisa menggunakan sesi ekspres alih - alih ini.
ujjal das

5

Inilah yang saya sarankan tanpa menggunakan dependensi lain, cukup simpul dan ekspres, gunakan app.locals, ini contohnya:

    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
Saya merasa ini adalah ide yang buruk karena req.app.locals tampaknya memengaruhi semua pengguna, bukan hanya yang membuat permintaan. Tentu Anda akan menghapus data segera setelah diberikan kepada pengguna, tetapi saya bayangkan Anda masih akan mengalami konflik dan menampilkan informasi yang salah dari waktu ke waktu. Dan jika Anda harus menghapusnya, Anda bisa menyimpannya di sesi saja.
stacker

1
sebenarnya itu hanya akan mempengaruhi permintaan yang dikirim oleh pengguna "unik", bahkan jika 2 atau lebih pengguna mengirim permintaan pada "waktu yang sama" setiap pengguna akan memiliki objek permintaannya sendiri dan objek penduduk lokalnya, dengan cara itu benar-benar aman untuk gunakan
Hussein Dimessi

req adalah untuk pengguna yang unik, tetapi req.app adalah untuk semua pengguna.
ZeroCho

gunakan express-session untuk mengirim pesan
ujjal das

3

Anda bisa melewatkan bit kecil dari data pasangan kunci / nilai melalui string kueri:

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

Dan javascript pada halaman beranda dapat mengaksesnya dan menyesuaikan perilakunya sesuai.

Perhatikan bahwa jika Anda tidak keberatan /categorytetap sebagai URL di bilah alamat peramban, Anda bisa langsung merendernya alih-alih mengarahkan. IMHO berkali-kali orang menggunakan arahan ulang karena kerangka kerja web lama dibuat langsung merespons sulit, tetapi mudah diungkapkan:

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

  // Process the data received in req.body

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

Sebagai @ Dropped.on.Caprica berkomentar, menggunakan AJAX menghilangkan kekhawatiran mengubah URL.


Seperti yang saya katakan di jawaban lain, string kueri sepertinya tidak bagus, karena saya belum tahu seberapa besar data yang ingin saya sampaikan. Solusi Anda yang lain adalah yang lebih menyenangkan yang saya temukan, tetapi masih mematahkan niat saya untuk menjaga URL yang sama.
Enrique Moreno Tent

@Dbugger Gunakan AJAX POST mungkin?
AlbertEngelB

1

kita bisa menggunakan sesi ekspres untuk mengirim data yang diperlukan

ketika Anda menginisialisasi aplikasi

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

jadi sebelum pengalihan hanya menyimpan konteks untuk sesi ini

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

Sekarang Anda bisa mendapatkan konteks di mana saja untuk sesi ini. itu bisa hanya dengan req.session.context

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

    // So prepare the context
var context=req.session.context;
    res.render('home.jade', context);
});
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.