Jak przekierować w expressjs, przekazując jakiś kontekst?

269

Używam ekspresu, aby stworzyć aplikację internetową w node.js. Jest to uproszczenie tego, co mam:

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

Mój problem jest następujący:

Jeśli stwierdzę, że dane zostały wysłane /category nie sprawdzają się, chciałbym przekazać stronie dodatkowy kontekst /. Jak mogłem to zrobić? Wydaje się, że przekierowanie nie pozwala na żaden dodatkowy parametr.

Namiot Enrique Moreno
źródło
1
Check out express ' flash: stackoverflow.com/questions/12442716/…
tymeJV
Ważna jest tutaj terminologia: nie ma /strony. Istnieje /trasa, którą Express może obsłużyć, i istnieje szablon strony głównej, który Express może renderować. Jeśli chcesz zachować niektóre dane do renderowania szablonu strony głównej po przekierowaniu, pomimo zaakceptowanej odpowiedzi sesje są Twoim przyjacielem. Dają ci magazyn danych, który utrzymuje się wśród żądań i odpowiedzi dla poszczególnych użytkowników przeglądających, dzięki czemu możesz wprowadzić dane podczas /categoryobsługi, a następnie wyjąć je, jeśli są tam podczas /przekazywania.
Mike „Pomax” Kamermans

Odpowiedzi:

366

Istnieje kilka sposobów przekazywania danych na różne trasy. Najbardziej poprawną odpowiedzią są oczywiście ciągi zapytań. Musisz upewnić się, że wartości są prawidłowo encodeURIComponent i decodeURIComponent .

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

Możesz złapać to na drugiej trasie, wysyłając parametry za pomocą req.query.

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

Aby uzyskać bardziej dynamiczny sposób, możesz użyć urlmodułu podstawowego do wygenerowania ciągu zapytania:

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

Więc jeśli chcesz przekierować wszystkie zmienne ciągu zapytania, możesz to zrobić

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

A jeśli używasz Węzła> = 7.x, możesz również użyć querystringmodułu podstawowego

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

Innym sposobem na zrobienie tego jest ustawienie czegoś w sesji. Tutaj możesz przeczytać, jak to ustawić , ale ustawianie i uzyskiwanie dostępu do zmiennych jest mniej więcej takie:

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

A później po przekierowaniu ...

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

Istnieje również możliwość korzystania starą cechę Express req.flash. W nowszych wersjach Express będzie to wymagało użycia innej biblioteki. Zasadniczo pozwala skonfigurować zmienne, które będą się wyświetlać i resetować przy następnej wizycie na stronie. Przydaje się do wyświetlania błędów użytkownikom, ale ponownie jest domyślnie usuwany. EDYCJA: Znaleziono bibliotekę, która dodaje tę funkcjonalność.

Mam nadzieję, że da to ogólny pomysł na przekazywanie informacji w aplikacji Express.

AlbertEngelB
źródło
6
Kwerendy nie są w porządku, jeśli chodzi o przejście kontekstu, ponieważ może być tak duży jak długi akapit lub nawet więcej. Jeśli chodzi o sesje, to może działać. ale to naprawdę nie wydaje się właściwe, ponieważ nie wydaje się to właściwym celem sesji ...
Namiot Enrique Moreno
@Dbugger Ups, nie zdawałem sobie sprawy, że zadałeś pytanie. Jeśli martwisz się o przesyłanie zbyt dużej ilości informacji do użytkownika za pomocą kwerendy, lepiej przechowywać dane sprawdzania poprawności w bazie danych, a następnie zapytać o to, gdy wybierzesz główną /trasę. Inną opcją jest wywołanie trasy pocztowej za pośrednictwem AJAX i zapisanie powstałych danych w magazynie lokalnym lub w podobny sposób.
AlbertEngelB
13
Dobra odpowiedź, ale myślę, że sesje to lepsze podejście niż ciągi zapytań - nie ujawniaj treści w adresach URL!
user2562923
2
@ user2562923 Zgadzam się, sesje lub testy POST są lepsze niż GET do przekazywania kontekstu. Odpowiedź była dość niejasna, więc nie byłem pewien, jakie informacje przekazuje pytający.
AlbertEngelB
węzeł js ma 2 podstawowe moduły, które generują ciąg zapytania
Fareed Alnamrouti
42

Najłatwiejszy sposób, jaki udało mi się przekazać do przesyłania danych między routeHandlerami, aby next()nie trzeba było bałagać się przekierowaniami ani sesjami. Opcjonalnie możesz po prostu zadzwonić homeCtrl(req,res)zamiast next()i po prostu przekazać reqires

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);
jqualls
źródło
4
Kolego, uwielbiam tę odpowiedź. Pomógł mi wydostać się z dziury - chociaż naprawdę muszę więcej czytać, ponieważ moje jelita wołały, abym przekazał wymagania i res jako argumenty dla next ().
JonRed
1
Bardzo dobra i konserwatywna odpowiedź! O wiele łatwiejsze i bezpieczniejsze niż inne odpowiedzi.
Triforcey
2
To podejście wydaje się być czystsze, ale nie jestem pewien, czy opuści pasek adresu w najnowszej ścieżce, czy też zakończy się o/
Danielo515
1
@ Danielo515 niestety skończy się w / kategoriach, więc w rzeczywistości jest to to samo, co renderowanie widoku bezpośrednio w / kategorie ...
Manuel Graf
2
Nie zgadzam się, że używasz routera jako usługi do renderowania odmiennego widoku, który opuszcza adres URL, ponieważ źle jest ustrukturyzować kod w ten sposób, celem następnego jest przejście do następnego oprogramowania pośredniego i zwykle
oddzielić
9

Musiałem znaleźć inne rozwiązanie, ponieważ żadne z dostarczonych rozwiązań nie spełniało moich wymagań z następujących powodów:

  • Ciągi zapytań : możesz nie chcieć używać ciągów zapytań, ponieważ adresy URL mogą być współużytkowane przez użytkowników, a czasem parametry zapytania nie mają sensu dla innego użytkownika. Na przykład błąd, który ?error=sessionExpirednigdy nie powinien zostać wyświetlony innemu użytkownikowi.

  • req.session : Możesz nie chcieć używać, req.sessionponieważ potrzebujesz do tego zależności ekspresowej sesji , która obejmuje skonfigurowanie magazynu sesji (takiego jak MongoDB), który może nie być w ogóle potrzebny, a może już używasz niestandardowego rozwiązanie sklepu sesyjnego.

  • next () : Możesz nie chcieć używać next()lub next("router")ponieważ to zasadniczo renderuje twoją nową stronę pod oryginalnym adresem URL, tak naprawdę nie jest to przekierowanie do nowego adresu URL, bardziej jak do przodu / do tyłu, co może być nie do przyjęcia.

To moje czwarte rozwiązanie, które nie cierpi na żadne z poprzednich problemów. Zasadniczo wiąże się to z użyciem tymczasowego pliku cookie, dla którego musisz najpierw zainstalować parser plików cookie . Oczywiście oznacza to, że będzie działać tylko przy włączonych plikach cookie i przy ograniczonej ilości danych.

Przykład realizacji:

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("/");
}
cprcrack
źródło
Magazyn sesji nie jest wymagany express-session. Baza danych po prostu sprawia, że ​​sesje trwają po ponownym uruchomieniu serwera.
Mike „Pomax” Kamermans
6

użyj app.set i app.get

Ustawianie danych

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

Pobieranie danych

router.get("/sign", (req, res) => {
  console.log('sign', req.app.get('user'))
});
UA_
źródło
2
zły pomysł, aby go wdrożyć. może wpływać na wszystkich użytkowników. Wszystkie wymagania są takie same dla wszystkich użytkowników. zamiast tego możemy użyć sesji ekspresowej .
ujjal das
5

Oto, co proponuję bez używania jakiejkolwiek innej zależności, wystarczy węzeł i ekspres, użyj app.locals, oto przykład:

    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("/");
    }
Hussein Dimessi
źródło
2
Wydaje mi się, że to zły pomysł, ponieważ req.app.locals wydaje się wpływać na wszystkich użytkowników, nie tylko tych, którzy złożyli żądanie. Jasne, że wyczyścisz dane, gdy tylko zostaną przekazane użytkownikowi, ale wyobrażam sobie, że nadal będziesz mieć konflikt i od czasu do czasu wyświetlać nieprawidłowe informacje. A jeśli i tak musisz go wyczyścić, równie dobrze możesz po prostu zapisać go w sesji.
układarki
1
w rzeczywistości wpłynie tylko na żądanie wysłane przez „unikalnych” użytkowników, nawet jeśli 2 lub więcej użytkowników wysyła żądania w tym samym czasie, każdy użytkownik będzie miał swój własny obiekt żądania i obiekt lokalny, w ten sposób jest całkowicie bezpieczny zastosowanie
Hussein Dimessi
req jest dla unikalnego użytkownika, ale req.app jest dla wszystkich użytkowników.
ZeroCho,
użyj sesji ekspresowej, aby wysłać wiadomość
ujjal das
3

Możesz przekazać małe bity danych para klucz / wartość przez ciąg zapytania:

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

I javascript na stronie głównej może uzyskać do niego dostęp i odpowiednio dostosować jego zachowanie.

Pamiętaj, że jeśli nie masz nic przeciwko /categorypozostaniu jako adres URL w pasku adresu przeglądarki, możesz po prostu renderować bezpośrednio zamiast przekierowywać. IMHO wiele razy ludzie używają przekierowań, ponieważ starsze frameworki internetowe utrudniały bezpośrednią odpowiedź, ale jest to łatwe w ekspresowym:

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

  // Process the data received in req.body

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

Jak skomentował @ Dropped.on.Caprica, użycie AJAX eliminuje problem zmiany adresu URL.

Peter Lyons
źródło
Jak powiedziałem w innej odpowiedzi, ciągi zapytań nie wydają się dobre, ponieważ nie wiem jeszcze, jak duże są dane, które chcę przekazać. Twoje inne rozwiązanie jest bardziej przyjazne dla mnie, ale nadal łamie moją intencję utrzymania tego samego adresu URL.
Namiot Enrique Moreno,
@Dbugger Użyć AJAX POST?
AlbertEngelB
1

możemy użyć sesji ekspresowej aby wysłać wymagane dane

podczas inicjalizacji aplikacji

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

więc przed przekierowaniem zapisz kontekst dla sesji

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

Teraz możesz uzyskać kontekst w dowolnym miejscu sesji. można to uzyskać tylko przez req.session.context

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

    // So prepare the context
var context=req.session.context;
    res.render('home.jade', context);
});
ujjal das
źródło