Tham số nào tiếp theo được sử dụng trong Express là gì?


295

Giả sử bạn có một khối mã đơn giản như thế này:

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

Hàm này có hai tham số, reqres, đại diện cho các đối tượng yêu cầu và đáp ứng tương ứng.

Mặt khác, có các hàm khác với tham số thứ ba được gọi next. Ví dụ: hãy xem đoạn mã sau:

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

Tôi không thể hiểu ý nghĩa của next()nó là gì hoặc tại sao nó được sử dụng. Trong ví dụ đó, nếu id không tồn tại, nextthực tế thì đang làm gì?


13
Tiếp theo chỉ đơn giản là cho phép trình xử lý tuyến tiếp theo trong dòng xử lý yêu cầu. Trong trường hợp này, nếu id người dùng tồn tại, nó có thể sẽ sử dụng res.sendđể hoàn thành yêu cầu. Nếu nó không tồn tại, có khả năng một trình xử lý khác sẽ phát sinh lỗi và hoàn thành yêu cầu sau đó.
Đaminh Barnes

1
vì vậy, bạn đang nói rằng tôi có một cái app.post('/login',function(req,res))sau app.get('/users',function(req,res)) nó sẽ gọi đăng nhập là tuyến tiếp theo trong tệp app.js bằng cách gọi next ()?
Menztrual

2
Không, bạn nên tham khảo phần này của tài liệu Express.js: expressjs.com/guide.html#passing-route control
Dominic Barnes

3
Về cơ bản, tuyến tiếp theo sẽ được chạy sẽ là một tuyến khác mà URL cho yêu cầu khớp. Trong trường hợp này, nếu một tuyến khác đã được đăng ký thông qua app.get("/users"), thì nó sẽ được chạy nếu xử lý các cuộc gọi bên trên.
Đaminh Barnes

3
Tiếp theo về cơ bản chỉ là một cuộc gọi lại.
Jonathan Ong

Câu trả lời:


266

Nó vượt qua sự kiểm soát để tuyến đường phù hợp tiếp theo . Ví dụ, trong ví dụ bạn đưa ra, bạn có thể tra cứu người dùng trong cơ sở dữ liệu nếu idđược cung cấp và gán nó cho req.user.

Dưới đây, bạn có thể có một tuyến đường như:

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

Vì / users / 123 sẽ khớp tuyến đường trong ví dụ của bạn trước, nên trước tiên sẽ kiểm tra và tìm người dùng 123; sau đó /userscó thể làm một cái gì đó với kết quả của điều đó.

Tuy nhiên, theo tôi, phần mềm trung gian là một công cụ linh hoạt và mạnh mẽ hơn vì nó không phụ thuộc vào sơ đồ URI cụ thể hoặc thứ tự tuyến. Tôi có khuynh hướng mô hình hóa ví dụ được hiển thị như thế này, giả sử một Usersmô hình có async 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);
});

Có thể kiểm soát dòng chảy như thế này là khá tiện dụng. Bạn có thể muốn có một số trang nhất định chỉ có sẵn cho người dùng có cờ quản trị viên:

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

Hy vọng điều này đã cho bạn một số cảm hứng!


Bạn có thể nói rằng! Tuy nhiên, tôi sẽ có xu hướng làm điều này với phần mềm trung gian tuyến , vì nó không kết hợp logic với một thứ tự cụ thể của các tuyến hoặc các cấu trúc URI cụ thể.
Asherah

5
tại sao đôi khi bạn quay lại tiếp theo () nhưng đôi khi không
John

6
@ John: giá trị trả lại thực sự bị bỏ qua; Tôi chỉ muốn quay lại đó để đảm bảo tôi không gọi next()lại. Nó sẽ giống nhau nếu tôi chỉ sử dụng next(new Error(…)); return;.
Asherah

1
@ level0: giá trị trả về bị bỏ qua; bạn có thể xem xét nó tốc ký cho next(new Error(…)); return;. Nếu chúng ta chuyển một giá trị cho next, nó đơn phương coi là một lỗi . Tôi đã không nhìn vào mã tốc hành quá nhiều, nhưng hãy tìm hiểu kỹ và bạn sẽ tìm thấy những gì bạn cần :)
Asherah

1
@ level0: (Tôi đã đổi return next(…);thành next(…); return;để nó bớt khó hiểu hơn.)
Asherah

87

Tôi cũng có vấn đề hiểu tiếp theo (), nhưng điều này đã giúp

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

3
Rất súc tích! Cảm ơn! Nhưng làm thế nào để bạn chắc chắn rằng cái đầu tiên .getđược gọi chứ không phải cái thứ hai?
JohnnyQ

18
@JohnnyQ Nó sẽ được thực hiện từ trên xuống dưới
Tapash

59

Trước khi hiểu next, bạn cần có một ý tưởng nhỏ về chu trình Yêu cầu-Phản hồi trong nút mặc dù không có nhiều chi tiết. Nó bắt đầu bằng việc bạn thực hiện một yêu cầu HTTP cho một tài nguyên cụ thể và nó kết thúc khi bạn gửi phản hồi lại cho người dùng tức là khi bạn gặp phải một cái gì đó như res.send ('Hello World');

Hãy xem một ví dụ rất đơn giản.

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

Ở đây chúng ta không cần next (), bởi vì resp.send sẽ kết thúc chu kỳ và bàn giao lại quyền điều khiển cho phần mềm trung gian tuyến.

Bây giờ hãy xem một ví dụ khác.

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

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

Ở đây chúng ta có 2 hàm trung gian cho cùng một đường dẫn. Nhưng bạn sẽ luôn nhận được phản hồi từ người đầu tiên. Bởi vì điều đó được gắn đầu tiên trong ngăn xếp phần mềm trung gian và res.send sẽ kết thúc chu kỳ.

Nhưng nếu chúng ta luôn không muốn thế giới Hello Hello !!!! phản hồi lại. Đối với một số điều kiện, chúng tôi có thể muốn "Xin chào hành tinh !!!!" phản ứng. Hãy sửa đổi mã trên và xem điều gì sẽ xảy ra.

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

Những gì nextđang làm ở đây. Và vâng, bạn có thể có những tiếng kêu. Nó sẽ bỏ qua chức năng phần mềm trung gian đầu tiên nếu điều kiện là đúng và gọi chức năng phần mềm trung gian tiếp theo và bạn sẽ có "Hello Planet !!!!"phản hồi.

Vì vậy, tiếp theo chuyển điều khiển cho chức năng tiếp theo trong ngăn xếp phần mềm trung gian.

Điều gì sẽ xảy ra nếu chức năng phần mềm trung gian đầu tiên không gửi lại bất kỳ phản hồi nào nhưng thực hiện một đoạn logic và sau đó bạn nhận được phản hồi từ chức năng phần mềm trung gian thứ hai.

Một cái gì đó như dưới đây: -

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

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

Trong trường hợp này, bạn cần cả hai hàm trung gian được gọi. Vì vậy, cách duy nhất bạn đạt được chức năng phần mềm trung gian thứ hai là bằng cách gọi next ();

Điều gì xảy ra nếu bạn không thực hiện cuộc gọi tiếp theo. Đừng mong đợi chức năng phần mềm trung gian thứ hai sẽ được gọi tự động. Sau khi gọi chức năng đầu tiên, yêu cầu của bạn sẽ bị treo. Hàm thứ hai sẽ không bao giờ được gọi và bạn sẽ không nhận được phản hồi.


Vì vậy, next()thực hiện như một gotovới một nhãn cứng? Đó là, trong đoạn mã thứ ba của bạn, một khi bạn gọi next(), res.send("Hello World !!!!"); sẽ không bao giờ được thực thi? Tôi nhận thấy rằng @Ashe luôn có một cuộc gọi return;sau nextcó mã trong cùng một cây thực thi ... Đoán tôi luôn có thể kiểm tra nhanh, hả? / chạy đến trình soạn thảo văn bản của mình;)
ruffin

@ruffin vâng, bạn có thể nghĩ giống như tiếp theo với một goto. nhưng tiếp theo biết nơi để đi không giống như goto đòi hỏi phải có nhãn. Tiếp theo sẽ chuyển điều khiển sang chức năng phần mềm trung gian tiếp theo. Ngoài ra, bạn có thể đặt tên 'tiếp theo' bất cứ điều gì bạn thích. Nó chỉ là một nhãn hiệu ở đây. Nhưng cách tốt nhất là sử dụng tên 'tiếp theo'
Mav55

3
Được rồi, có vẻ như không chính xác. Tôi đã thử mã ( pastebin ở đây ) và mã sau khi next()cuộc gọi được gọi . Trong trường hợp này, past the next() callđược ghi vào bàn điều khiển, và sau đó tôi gặp Error: Can't set headers after they are sent.lỗi, vì lần thứ hai res.sendđược gọi, mặc dù không thành công. Luồng mã sẽ quay trở lại sau next()cuộc gọi, điều này làm cho @ Ashe returns(hoặc quản lý logic khác) trở nên quan trọng.
ruffin

4
@ruffin, vâng bạn nói đúng. Chúng ta cần một câu lệnh return sau khi next()bỏ qua việc thực thi các câu lệnh còn lại. cảm ơn đã chỉ ra rằng
Mav55

1
Cảm ơn bạn đã thực sự giải thích "phần mềm trung gian" là gì / không có ví dụ rõ ràng và không chỉ làm vẹt tài liệu. Đây là câu trả lời duy nhất thực sự nói bất cứ điều gì rõ ràng về những gì xảy ra, tại sao & như thế nào.
mc01

11

Tiếp theo được sử dụng để chuyển điều khiển sang chức năng phần mềm trung gian tiếp theo. Nếu không yêu cầu sẽ được treo hoặc mở.


6
Tôi không chắc câu trả lời này thêm vào câu hỏi gần bảy năm tuổi ...
mherzig

Mặc dù ngắn gọn, nhận xét này dẫn tôi đến điều này: expressjs.com/en/guide/wr-middleware.html
hmak

@mherzig, nó là một lớp lót và bao gồm mọi thứ
M. Gopal

5

Gọi chức năng này gọi chức năng trung gian tiếp theo trong ứng dụng. Hàm next () không phải là một phần của Node.js hoặc Express API, nhưng là đối số thứ ba được truyền cho hàm phần mềm trung gian. Hàm next () có thể được đặt tên bất cứ thứ gì, nhưng theo quy ước, nó luôn được đặt tên là tiếp theo.


2

Thực thi nextchức năng thông báo cho máy chủ rằng bạn đã hoàn thành bước trung gian này và nó có thể thực hiện bước tiếp theo trong chuỗi.

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.