Expressで使用される「次」のパラメータは何ですか?


295

次のような単純なコードブロックがあるとします。

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

この関数には、要求オブジェクトと応答オブジェクトをそれぞれ表す2つのパラメーターreqresがあります。

一方、と呼ばれる3番目のパラメーターを持つ他の関数がありますnext。たとえば、次のコードを見てみましょう。

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

何のポイントなのか、next()なぜ使われているのか理解できません。その例では、idが存在しない場合、next実際には何をしていますか?


13
Nextは、単に次のルートハンドラーが要求を処理できるようにします。この場合、ユーザーIDが存在する場合、それを使用res.sendしてリクエストを完了する可能性があります。存在しない場合は、エラーを発行してリクエストを完了する別のハンドラが存在する可能性があります。
ドミニクバーンズ

1
それで、next()を呼び出すことによって、app.jsファイルの次のルートであるloginを呼び出すapp.post('/login',function(req,res))後があると言っていapp.get('/users',function(req,res)) ますか?
Menztrual

2
いいえ、Express.jsドキュメントのこの部分を参照する必要があります。expressjs.com / guide.html#passing
Dominic Barnes

3
基本的に、実行される次のルートは、リクエストのURLが一致する別のルートになります。この場合は、別のルートが経由して登録された場合はapp.get("/users")、次の呼び出し上記のハンドラならば、それが実行されます。
ドミニクバーンズ

3
次は基本的にただのコールバックです。
Jonathan Ong

回答:


266

制御を次に一致するルートに渡します。たとえば、指定した例では、idが指定されている場合はデータベースでユーザーを検索し、それをに割り当てreq.userます。

以下では、次のようなルートが考えられます。

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

/ users / 123は最初に例のルートと一致するため、最初にuserをチェックして見つけます123。その後/users、その結果で何かを行うことができます。

ルートミドルウェアは、それが特定のURIスキームまたはルートの順序に依存しないため、私の意見では、しかし、より柔軟で強力なツールです。私はUsers、非同期のモデルを想定して、このような例をモデル化する傾向があります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);
});

このようにフローを制御できると便利です。特定のページを、管理フラグを持つユーザーのみが利用できるようにしたい場合があります。

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

これがあなたにインスピレーションを与えたことを願っています!


あなたはそれを言うことができます!ただし、ロジックを特定の順序のルートや特定のURI構造に結び付けないため、ルートミドルウェアを使用してこのようなことを行う傾向があります。
アシェラ2012年

5
なぜときどきnext()を返すが、ときどき戻らない
John

6
@John:戻り値は実際には無視されます。next()二度と電話しないように、そこに戻りたいだけです。そのまま使用しても同じですnext(new Error(…)); return;
Asherah

1
@ level0:戻り値は無視されます。あなたはそれを省略形と考えることができますnext(new Error(…)); return;。我々はに値を渡すとnextそれは一方的にエラーと考えられています。私はエクスプレスコードをあまり調べていませんが、調べてみると、必要なものが見つかります:)
Asherah

1
@ level0:(混乱しにくいように変更return next(…);しましたnext(…); return;。)
Asherah

87

next()の理解にも問題がありましたが、これ

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
とても簡潔です!ありがとう!しかし、どのようにして.get、2つ目ではなく1つ目が呼び出されることを確認しますか?
JohnnyQ 2017

18
@JohnnyQそれは上から下への実行になります
Tapash

59

を理解する前にnext、ノードのリクエスト/レスポンスサイクルについて少し理解する必要がありますが、詳細はあまりわかりません。これは、特定のリソースに対してHTTPリクエストを作成することから始まり、ユーザーに応答を返すとき、つまりres.send( 'Hello World');のようなものに遭遇したときに終了します。

非常に簡単な例を見てみましょう。

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

resp.sendはサイクルを終了し、コントロールをルートミドルウェアに戻すため、ここではnext()は必要ありません。

次に、別の例を見てみましょう。

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

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

ここでは、同じパスに対して2つのミドルウェア関数があります。しかし、あなたはいつも最初のものから応答を受け取るつもりです。それはミドルウェアスタックの最初にマウントされ、res.sendがサイクルを終了するためです。

しかし、「Hello World !!!!」を常に望んでいない場合はどうでしょうか。返答。条件によっては、「Hello Planet !!!!」が必要になる場合があります。応答。上記のコードを変更して、何が起こるか見てみましょう。

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

nextここで何をしていますか。そして、はい、あなたはガスを持っているかもしれません。条件が真の場合、最初のミドルウェア関数をスキップし、次のミドルウェア関数を呼び出すと、"Hello Planet !!!!"応答が返されます。

したがって、次に、ミドルウェアスタック内の次の関数に制御を渡します。

最初のミドルウェア関数が応答を返さず、ロジックの一部を実行し、次に2番目のミドルウェア関数から応答を受け取った場合はどうなるでしょう。

以下のようなもの:-

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

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

この場合、両方のミドルウェア機能を呼び出す必要があります。したがって、2番目のミドルウェア関数に到達する唯一の方法は、next()を呼び出すことです。

次への呼び出しを行わないとどうなるでしょう。2番目のミドルウェア関数が自動的に呼び出されることを期待しないでください。最初の関数を呼び出した後、リクエストはハングしたままになります。2番目の関数は呼び出されず、応答も返されません。


だから、next()のような実行するgotoハードワイヤードラベルとは?つまり、3番目のスニペットでは、を呼び出すとnext()res.send("Hello World !!!!"); 決して実行されませんか?@Asheは常に同じ実行ツリー内にコードが含まれているreturn;after next呼び出しがあったことに気づきました...エクスプレスを常にチェックインできると思いませんか?/彼のテキストエディタに移動します;)
ruffin

@ruffinはい、次のgotoのようなものと考えることができます。しかし次は、ラベルを必要とするgotoとは異なり、どこに行くべきかを知っています。Nextは、次のミドルウェア機能に制御を渡します。また、「次」には好きな名前を付けることができます。ここは単なるラベルです。しかし、ベストプラクティスは、「次へ」という名前を使用することです
Mav55

3
さて、それは正確ではないようです。私はコード(ここではペーストビン)を試しましたが、next()呼び出し後のコードが呼び出されます。この場合は、past the next() callコンソールに書き込まれ、その後、私が取得Error: Can't set headers after they are sent.秒は以下のように、エラーをres.sendしかし失敗した、と呼ばれています。コードフローnext()呼び出しの後に戻ります。そのため、@ Ashe returns(または他のロジック管理)が重要になります。
ルフィン、

4
@ruffin、そうだね。next()残りのステートメントの実行をスキップするには、の後にreturnステートメントが必要です。それを指摘してくれてありがとう。
Mav55 2018年

1
ドキュメンテーションだけでなく、「ミドルウェア」が何であるか/明確な例が含まれているかを実際に説明していただきありがとうございます。これが、何が起こったのか、なぜ、どのように起こったのかについて明確なことを実際に言う唯一の答えでした。
mc01

11

Nextは、次のミドルウェア機能に制御を渡すために使用されます。そうでない場合、リクエストは保留またはオープンのままになります。


6
この答えがほぼ7年前の質問に何を追加するのか
わかりませ

簡潔ではありますが、このコメントはこれに私を導きました:expressjs.com/en/guide/writing-middleware.html
hmak

@mherzig、それは1つのライナーであり、すべてをカバーしています
M. Gopal

5

この関数を呼び出すと、アプリ内の次のミドルウェア関数が呼び出されます。next()関数はNode.jsまたはExpress APIの一部ではありませんが、ミドルウェア関数に渡される3番目の引数です。next()関数には任意の名前を付けることができますが、慣例により、常に「next」という名前が付けられています。


2

next関数を実行すると、このミドルウェアステップが完了し、チェーンの次のステップを実行できることがサーバーに通知されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.