Do czego służy parametr „następny” w programie Express?

295

Załóżmy, że masz prosty blok kodu taki jak ten:

app.get('/', function(req, res){
    res.send('Hello World');
});

Ta funkcja ma dwa parametry reqires , które przedstawiają żądania i odpowiedzi odpowiednio.

Z drugiej strony istnieją inne funkcje z trzecim parametrem o nazwie next. Na przykład spójrzmy na następujący kod:

app.get('/users/:id?', function(req, res, next){ // Why do we need next?
    var id = req.params.id;
    if (id) {
        // do something
    } else {
        next(); // What is this doing?
    }
});

Nie rozumiem, o co next()chodzi i dlaczego jest używany. W tym przykładzie, jeśli identyfikator nie istnieje, co nextwłaściwie robi?

Menztrual
źródło
13
Next po prostu pozwala następnemu modułowi obsługi trasy w linii obsłużyć żądanie. W takim przypadku, jeśli identyfikator użytkownika istnieje, prawdopodobnie użyje go res.senddo wykonania żądania. Jeśli nie istnieje, prawdopodobnie istnieje inny moduł obsługi, który wygeneruje błąd i wtedy wypełni żądanie.
Dominic Barnes
1
tak mówisz Mam app.post('/login',function(req,res))po app.get('/users',function(req,res)) nim zadzwoni logowanie jest kolejna trasa w app.js pliku przez wywołanie next ()?
Menztrual
2
Nie, powinieneś zapoznać się z tą częścią dokumentacji Express.js: expressjs.com/guide.html#passing-route control
Dominic Barnes
3
Zasadniczo następną trasą do uruchomienia będzie inna, która odpowiada adresowi URL żądania. W takim przypadku, jeśli zarejestrowano inną trasę app.get("/users"), zostanie ona uruchomiona, jeśli program obsługi powyżej wywoła następnie.
Dominic Barnes
3
Następny jest w zasadzie tylko oddzwanianiem.
Jonathan Ong

Odpowiedzi:

266

Przekazuje kontrolę nad następną pasującą trasą. W podanym przykładzie możesz na przykład wyszukać użytkownika w bazie danych idi przypisać go do req.user.

Poniżej możesz wyznaczyć trasę taką jak:

app.get('/users', function(req, res) {
  // check for and maybe do something with req.user
});

Ponieważ / users / 123 najpierw dopasuje trasę w twoim przykładzie, najpierw sprawdzi i znajdzie użytkownika 123; wtedy /usersmoże coś z tym zrobić.

Oprogramowanie pośredniczące trasy jest jednak, moim zdaniem, bardziej elastycznym i wydajnym narzędziem, ponieważ nie zależy od konkretnego schematu URI ani kolejności tras. Byłbym skłonny do modelowania pokazanego przykładu tak, zakładając Usersmodel z asynchronią findOne():

function loadUser(req, res, next) {
  if (req.params.userId) {
    Users.findOne({ id: req.params.userId }, function(err, user) {
      if (err) {
        next(new Error("Couldn't find user: " + err));
        return;
      }

      req.user = user;
      next();
    });
  } else {
    next();
  }
}

// ...

app.get('/user/:userId', loadUser, function(req, res) {
  // do something with req.user
});

app.get('/users/:userId?', loadUser, function(req, res) {
  // if req.user was set, it's because userId was specified (and we found the user).
});

// Pretend there's a "loadItem()" which operates similarly, but with itemId.
app.get('/item/:itemId/addTo/:userId', loadItem, loadUser, function(req, res) {
  req.user.items.append(req.item.name);
});

Możliwość kontrolowania przepływu w ten sposób jest bardzo przydatna. Możesz chcieć, aby niektóre strony były dostępne tylko dla użytkowników z flagą administratora:

/**
 * Only allows the page to be accessed if the user is an admin.
 * Requires use of `loadUser` middleware.
 */
function requireAdmin(req, res, next) {
  if (!req.user || !req.user.admin) {
    next(new Error("Permission denied."));
    return;
  }

  next();
}

app.get('/top/secret', loadUser, requireAdmin, function(req, res) {
  res.send('blahblahblah');
});

Mam nadzieję, że to dało ci inspirację!

Asherah
źródło
Moglbys to powiedziec! Byłbym jednak bardziej skłonny robić takie rzeczy z oprogramowaniem pośredniczącym tras , ponieważ nie łączy ono logiki z określoną kolejnością tras lub konkretnymi strukturami URI.
Asherah
5
dlaczego czasami wracasz next (), a czasem nie
John
6
@John: zwracana wartość jest faktycznie ignorowana; Chcę tylko tam wrócić, aby upewnić się, że nie zadzwonię next()ponownie. Byłoby tak samo, gdybym tylko użył next(new Error(…)); return;.
Asherah,
1
@ poziom0: zwracana wartość jest ignorowana; możesz to uznać za skrót next(new Error(…)); return;. Jeśli przekażemy wartość next, jest to jednostronnie uważane za błąd . Nie za bardzo zagłębiałem się w kod ekspresowy, ale przekopaj się, a znajdziesz to, czego potrzebujesz :)
Asherah
1
@ poziom 0: (Zmieniłem return next(…);na, next(…); return;więc jest mniej mylące.)
Asherah
87

Miałem też problem ze zrozumieniem next (), ale to pomogło

var app = require("express")();

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write("Hello");
    next(); //remove this and see what happens 
});

app.get("/", function(httpRequest, httpResponse, next){
    httpResponse.write(" World !!!");
    httpResponse.end();
});

app.listen(8080);
Rajesk
źródło
3
Bardzo zwięzłe! Dzięki! Ale jak upewnić się, że pierwszy .getjest wywoływany, a nie drugi?
JohnnyQ
18
@JohnnyQ Będzie to wykonanie od góry do dołu
Tapash
59

Zanim zrozumiesz next, musisz mieć pojęcie o cyklu Zapytanie-Odpowiedź w węźle, ale nie za bardzo szczegółowo. Zaczyna się od zgłoszenia żądania HTTP dla określonego zasobu i kończy się, gdy odeślesz odpowiedź do użytkownika, tj. Gdy napotkasz coś takiego jak res.send („Hello World”);

spójrzmy na bardzo prosty przykład.

app.get('/hello', function (req, res, next) {
  res.send('USER')
})

Tutaj nie potrzebujemy next (), ponieważ resp.send zakończy cykl i przekaże sterowanie z powrotem do oprogramowania pośredniego trasy.

Teraz spójrzmy na inny przykład.

app.get('/hello', function (req, res, next) {
  res.send("Hello World !!!!");
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

Tutaj mamy 2 funkcje oprogramowania pośredniego dla tej samej ścieżki. Ale zawsze dostaniesz odpowiedź od pierwszego. Ponieważ jest to montowane najpierw na stosie oprogramowania pośredniego, a res.send zakończy cykl.

Ale co, jeśli zawsze nie chcemy „Hello World !!!!” odpowiedź z powrotem. W niektórych warunkach możemy chcieć „Hello Planet !!!!” odpowiedź. Zmodyfikujmy powyższy kod i zobaczmy, co się stanie.

app.get('/hello', function (req, res, next) {
  if(some condition){
    next();
    return;
  }
  res.send("Hello World !!!!");  
});

app.get('/hello', function (req, res, next) {
  res.send("Hello Planet !!!!");
});

Co nexttu robisz? I tak, możesz mieć fałdy. Pominie pierwszą funkcję oprogramowania pośredniego, jeśli warunek jest spełniony, i wywoła następną funkcję oprogramowania pośredniego, a otrzymasz "Hello Planet !!!!"odpowiedź.

Następnie przekaż formant do następnej funkcji na stosie oprogramowania pośredniego.

Co się stanie, jeśli pierwsza funkcja oprogramowania pośredniego nie odeśle żadnej odpowiedzi, ale wykona logikę, a następnie otrzymasz odpowiedź z drugiej funkcji oprogramowania pośredniego.

Coś jak poniżej: -

app.get('/hello', function (req, res, next) {
  // Your piece of logic
  next();
});

app.get('/hello', function (req, res, next) {
  res.send("Hello !!!!");
});

W takim przypadku musisz wywołać obie funkcje oprogramowania pośredniego. Tak więc jedynym sposobem na przejście do drugiej funkcji oprogramowania pośredniego jest wywołanie next ();

Co jeśli nie wykonasz połączenia do następnego. Nie należy oczekiwać, że druga funkcja oprogramowania pośredniego zostanie wywołana automatycznie. Po wywołaniu pierwszej funkcji żądanie pozostanie zawieszone. Druga funkcja nigdy nie zostanie wywołana i nie otrzymasz odpowiedzi.

Mav55
źródło
Więc next()działa jak gotoz przewodową wytwórnią? Oznacza to, że w swoim trzecim fragmencie, kiedy zadzwonisz next(), res.send("Hello World !!!!"); nigdy nie zostanie stracony? Zauważyłem, że @Ashe zawsze miała wywołania return;po, nextktóre miały kod w tym samym drzewie wykonania ... Chyba zawsze mógłbym po prostu sprawdzić ekspresowo, co? / biegnie do swojego edytora tekstu;)
ruffin
@ruffin tak, możesz pomyśleć o następnym podobnym do goto. ale dalej wie, gdzie się udać w przeciwieństwie do goto, które wymaga etykiety. Dalej przekaże kontrolę do następnej funkcji oprogramowania pośredniego. Możesz także nazwać „następny”, co tylko chcesz. To tylko etykieta tutaj. Ale najlepszą praktyką jest używanie nazwy „dalej”
Mav55
3
Okej, wygląda na to, że to nie jest dokładne. Próbowałem kodu ( tutaj wklejanie ) i kod po next()wywołaniu jest wywoływany . W tym przypadku past the next() calljest zapisywany na konsoli, a następnie pojawia się Error: Can't set headers after they are sent.błąd, ponieważ drugi res.sendjest wywoływany, choć bezskutecznie. Przepływ kodu powraca po next()wywołaniu, co sprawia, że ​​@ Ashe returns(lub inne zarządzanie logiką) jest ważne.
ruffin
4
@ruffin, tak masz rację. Potrzebujemy instrukcji return po, next()aby pominąć wykonanie pozostałych instrukcji. dzięki za zwrócenie na to uwagi.
Mav55
1
Dziękujemy za wyjaśnienie, czym jest „oprogramowanie pośrednie” / czy zawiera jasne przykłady, a nie tylko dokumentowanie dokumentacji. To była jedyna odpowiedź, która faktycznie mówi coś jasnego o tym, co się dzieje, dlaczego i jak.
mc01,
11

Dalej służy do przekazania kontroli do następnej funkcji oprogramowania pośredniego. Jeśli nie, prośba pozostanie zawieszona lub otwarta.

Ronique Ricketts
źródło
6
Nie jestem pewien, co ta odpowiedź dodaje do prawie siedmioletniego pytania ...
mherzig
Chociaż krótki, ten komentarz doprowadził mnie do tego: expressjs.com/en/guide/writing-middleware.html
hmak
@mherzig, jest to jedna linijka i obejmuje wszystko
M. Gopal
5

Wywołanie tej funkcji wywołuje następną funkcję oprogramowania pośredniego w aplikacji. Funkcja next () nie jest częścią Node.js ani Express API, ale jest trzecim argumentem przekazywanym do funkcji oprogramowania pośredniego. Funkcja next () może mieć dowolną nazwę, ale zgodnie z konwencją zawsze nosi nazwę „next”.

Binayak Gouri Shankar
źródło
2

Wykonywanie nextfunkcji powiadamia serwer o zakończeniu tego kroku oprogramowania pośredniego i może wykonać następny krok w łańcuchu.

AGrush
źródło