Expressjsで実際にミドルウェアとapp.useは何を意味しますか?


228

私が目にするほとんどすべてのExpressアプリにはapp.useミドルウェアに関する記述がありますが、実際のミドルウェアとは何か、そのapp.use記述が何をしているかについての明確で簡潔な説明は見つかりませんでした。エクスプレスドキュメント自体も、これについては少しあいまいです。これらの概念について説明していただけますか?


3
参照のための同様の質問(これは、以前に作成されたカントー):stackoverflow.com/questions/11321635/...
ericsoco

43
^ Ha!これらの2つの質問は、コメントで互いに参照しています。
ジュリアンH.ラム

17
つまり、循環参照です。
Steve K

6
Express.js Middleware Demystifiedトピックに関する素晴らしいブログ投稿。これは以前ここでコピー貼り付けされていましたが、これはもちろん盗用ですが、元の投稿はまだ非常に役立つので、ここにリンクを残します。
totymedli

1
express.jsミドルウェアに関する記事を書きました。ここにリンクがあります:nodexplained.com/blog-detail/2017/12/31/…– shrawan_lakhe 18
1

回答:


111

ミドルウェア

新しいプロジェクトでミドルウェアの概念を分離する途中です。

ミドルウェアを使用すると、フローする必要のあるアクションのスタックを定義できます。Expressサーバー自体はミドルウェアのスタックです。

// express
var app = express();
// middleware
var stack = middleware();

次に、呼び出してミドルウェアスタックにレイヤーを追加できます。 .use

// express
app.use(express.static(..));
// middleware
stack.use(function(data, next) {
  next();
});

ミドルウェアスタックのレイヤーは関数で、n個のパラメーター(2つはexpress、reqres)とnext関数を受け取ります。

ミドルウェアは、レイヤーが何らかの計算を行い、パラメーターを増やしてからを呼び出すことを期待していますnext

スタックは、処理しない限り何もしません。Expressは、着信HTTP要求がサーバーでキャッチされるたびにスタックを処理します。ミドルウェアでは、スタックを手動で処理します。

// express, you need to do nothing
// middleware
stack.handle(someData);

より完全な例:

var middleware = require("../src/middleware.js");

var stack = middleware(function(data, next) {
    data.foo = data.data*2;
    next();
}, function(data, next) {
    setTimeout(function() {
        data.async = true;
        next();
    }, 100)
}, function(data) {
    console.log(data);
});

stack.handle({
    "data": 42
})

簡単に言うと、すべての着信HTTP要求に対して処理したい一連の操作を定義するだけです。

エクスプレス(接続ではなく)に関しては、グローバルミドルウェアがあり、特定のミドルウェアをルーティングします。つまり、すべての着信HTTPリクエストにミドルウェアスタックをアタッチするか、特定のルートと相互作用するHTTPリクエストにのみミドルウェアスタックをアタッチできます。

エクスプレス&ミドルウェアの高度な例:

// middleware 

var stack = middleware(function(req, res, next) {
    users.getAll(function(err, users) {
        if (err) next(err);
        req.users = users;
        next();  
    });
}, function(req, res, next) {
    posts.getAll(function(err, posts) {
        if (err) next(err);
        req.posts = posts;
        next();
    })
}, function(req, res, next) {
    req.posts.forEach(function(post) {
        post.user = req.users[post.userId];
    });

    res.render("blog/posts", {
        "posts": req.posts
    });
});

var app = express.createServer();

app.get("/posts", function(req, res) {
   stack.handle(req, res); 
});

// express

var app = express.createServer();

app.get("/posts", [
    function(req, res, next) {
        users.getAll(function(err, users) {
            if (err) next(err);
            req.users = users;
            next();  
        });
    }, function(req, res, next) {
        posts.getAll(function(err, posts) {
            if (err) next(err);
            req.posts = posts;
            next();
        })
    }, function(req, res, next) {
        req.posts.forEach(function(post) {
            post.user = req.users[post.userId];
        });

        res.render("blog/posts", {
            "posts": req.posts
        });
    }
], function(req, res) {
   stack.handle(req, res); 
});

4
うーん...この場合のミドルウェアはあなた自身のライブラリですか、またはエクスプレスの一部ですか?
iZ。

5
涼しい。私はまだapp.use()構文に少し混乱しています。ミドルウェアの実際の戻り値とは何useですか?
iZ。

9
@iZ useはスタックに追加します。次に、すべてのリクエストがスタックを通過します。
レイノス'09

7
@Raynos、プロジェクト「ミドルウェア」へのリンクが壊れています。

1
@Raynosですが、Expressでまだミドルウェアが使用されていますか?それが裸だとはどういう意味ですか?
Timo Huovinen、2013年

60

単純化すると、Webサーバーは、要求を受け取って応答を出力する機能と見なすことができます。したがって、Webサーバーを関数として見る場合、それをいくつかの部分に編成し、それらを小さな関数に分けて、それらの構成が元の関数になるようにすることができます。

ミドルウェアは、他のユーザーが作成できる小さな機能であり、それらを再利用できるという明らかな利点があります。


33

以前の回答で言及されていないものを追加するために、遅い回答を追加します。

これで、ミドルウェアがクライアント要求サーバー応答の間で実行される機能であることは明らかです。必要とされる最も一般的なミドルウェア機能は、エラー管理、データベースの相互作用、静的ファイルまたはその他のリソースからの情報の取得です。ミドルウェアスタックに移動するには、次のコールバックを呼び出す必要があります。ミドルウェア関数の最後でそれを確認して、フローの次のステップに移動できます。

あなたはapp.useアプローチを使用して、次のようなフローを持つことができます:

var express = require('express'),
    app = express.createServer(),                                                                                                                                                 
    port = 1337;

function middleHandler(req, res, next) {
    console.log("execute middle ware");
    next();
}

app.use(function (req, res, next) {
    console.log("first middle ware");                                                                                                             
    next();
});

app.use(function (req, res, next) {
    console.log("second middle ware");                                                                                                             
    next();
});

app.get('/', middleHandler, function (req, res) {
    console.log("end middleware function");
    res.send("page render finished");
});

app.listen(port);
console.log('start server');

ただし、別のアプローチを使用して、各ミドルウェアを関数の引数として渡すこともできます。以下は、MooTools Nodejs Webサイトの例です。ミドルウェアはresponse、クライアントに送り返される前にTwitter、Github、ブログのフローを取得します。関数が引数としてどのように渡されるかに注意してくださいapp.get('/', githubEvents, twitter, getLatestBlog, function(req, res){。using app.getはGETリクエストに対してのみapp.use呼び出され、すべてのリクエストに対して呼び出されます。

// github, twitter & blog feeds
var githubEvents = require('./middleware/githubEvents')({
    org: 'mootools'
});
var twitter = require('./middleware/twitter')();
var blogData = require('./blog/data');
function getLatestBlog(req, res, next){
    blogData.get(function(err, blog) {
        if (err) next(err);
        res.locals.lastBlogPost = blog.posts[0];
        next();
    });
}

// home
app.get('/', githubEvents, twitter, getLatestBlog, function(req, res){
    res.render('index', {
        title: 'MooTools',
        site: 'mootools',
        lastBlogPost: res.locals.lastBlogPost,
        tweetFeed: res.locals.twitter
    });
});

2
Express.jsがルートベース(ルーターベースではない)ミドルウェアのマウントをサポートしているかどうかに対する答えを探していましたか?答えでそれを示したようです。
セルチュク2016年

上記の例を説明できますか?どのように多くの関数をapp.get(...)に渡すことができ、それらはどの順序で呼び出されますか?
Tanner Summers

2
こんにちは@TannerSummers、この.get()メソッドは3種類の引数を取ります:最初、最後、中間の引数。内部的には、引数が2つ以上あるかどうかを検出し、それら(中央のもの)をミドルウェア関数として使用して、左から右に呼び出します。
セルジオ

22

expressjs ガイドはあなたの質問にかなりきちんとした答えを持っています、私はそれを読むことを強くお勧めします、私はガイドの短いスニペットを投稿しています、ガイドはかなり良いです。

Expressアプリで使用するミドルウェアを作成する

概観

ミドルウェア関数は、要求オブジェクト req)、応答オブジェクト res)、およびアプリケーションの要求-応答サイクルの次の関数にアクセスできる関数です。次の関数はExpressルーターの関数で、呼び出されると、現在のミドルウェアの後継となるミドルウェアを実行します。

ミドルウェア機能は、次のタスクを実行できます。

  • 任意のコードを実行します。
  • 要求オブジェクトと応答オブジェクトを変更します。
  • 要求/応答サイクルを終了します。
  • スタック内の次のミドルウェアを呼び出します。

現在のミドルウェア関数が要求/応答サイクルを終了しない場合、次のミドルウェア関数に制御を渡すためにnext()を呼び出す必要があります。それ以外の場合、リクエストは保留のままになります。

ここに画像の説明を入力してください

以下は、簡単な「Hello World」Expressアプリケーションの例です。この記事の残りの部分では、2つのミドルウェア関数を定義してアプリケーションに追加します。1つは単純なログメッセージを出力するmyLoggerと呼ばれ、もう1つはHTTPリクエストのタイムスタンプを表示するrequestTime 1と呼ばれます。

var express = require('express')
var app = express()

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

app.listen(3000)   

ミドルウェア関数myLogger

以下は、「myLogger」というミドルウェア関数の簡単な例です。この関数は、アプリへのリクエストが通過したときに「LOGGED」を出力するだけです。ミドルウェア関数は、myLoggerという名前の変数に割り当てられます。

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

上記のnext()の呼び出しに注意してください。この関数を呼び出すと、アプリ内の次のミドルウェア関数が呼び出されます。次の()関数は、Node.jsのかエクスプレスAPIの一部ではありませんが、ミドルウェアの関数に渡される第三引数です。次の()関数は、自由に名前を付けることができますが、慣例により、常に「次」と命名されます。混乱を避けるために、常にこの規則を使用してください。

ミドルウェア関数をロードするには、ミドルウェア関数を指定してapp.use()を呼び出します。たとえば、次のコードはルートパス(/)へのルートの前にmyLoggerミドルウェア関数をロードします。

var express = require('express')
var app = express()

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

app.use(myLogger)

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

app.listen(3000)

アプリはリクエストを受信するたびに、「LOGGED」というメッセージを端末に出力します。

ミドルウェアのロードの順序は重要です。最初にロードされるミドルウェア機能も最初に実行されます。

場合myLoggerは、ルート・パスへのルートの後にロードされ、要求がそれに到達したことがないし、ルートパスのルートハンドラが要求応答サイクルを終了させるためのアプリは、「LOGGED」に印刷されません。

ミドルウェア関数myLoggerは単にメッセージを出力してから、next()関数を呼び出すことにより、スタック内の次のミドルウェア関数に要求を渡します。


  1. この投稿にはmyLoggerミドルウェアのみが含まれます。今後の投稿については、こちらの元のexpressjsガイドにアクセスしてください。


1
とてもいい説明。
Drumbeg

エクスプレスサイトのこちらのexpressjs.com/en/guide/writing-middleware.htmlから入手できます。なぜ今まで誰も言及しなかったのかしら。
スラジュジャイン2018

2
良いですね。これは私がここで見た最も明確な説明であり、はい、だれもそれを参照していないことは奇妙です!
ドルンベグ2018

1
うまく

ミドルウェアのロードの順序は重要です。最初にロードされるミドルウェア機能も最初に実行されます。:これは非常に重要な注記です。他の答えはこれについて言及していません。Pythonのみに取り組んだ初心者にとって、これはこれまでに遭遇したことがない可能性があるため、非常に重要です。
Tessaracter

11

=====非常に簡単な説明=====

ミドルウェアはExpress.jsフレームワークのコンテキストで使用されることが多く、node.jsの基本概念です。簡単に言えば、基本的には、アプリケーションの要求オブジェクトと応答オブジェクトにアクセスできる関数です。私がそれについて考えたいのは、アプリケーションによって処理される前にリクエストが通過する一連の「チェック/事前画面」です。たとえば、ミドルウェアは、リクエストがアプリケーションに進む前に認証されているかどうかを判断し、リクエストが認証されていない場合、または各リクエストのロギングにログインページを返すのに適しています。さまざまな機能を実現する多くのサードパーティ製ミドルウェアが利用可能です。

単純なミドルウェアの例:

var app = express();
app.use(function(req,res,next)){
    console.log("Request URL - "req.url);
    next();
}

上記のコードは、入ってくるリクエストごとに実行され、リクエストのURLを記録します。next()メソッドは基本的にプログラムの続行を許可します。next()関数が呼び出されない場合、プログラムはそれ以上処理せず、ミドルウェアの実行で停止します。

いくつかのミドルウェアの問題点:

  1. リクエストは各ミドルウェアを順番に通過するため、アプリケーション内のミドルウェアの順序は重要です。
  2. ミドルウェア関数でnext()メソッドを呼び出すのを忘れると、リクエストの処理が停止する可能性があります。
  3. ミドルウェア機能のreqおよびresオブジェクトを変更すると、reqおよびresを使用するアプリケーションの他の部分で変更を利用できるようになります

1
どうもありがとうございました!これは、これを理解するのにこれまでのところ最良の説明です。質問は、私は、ミドルウェアとのいくつかのコードを読んでいますし、それが呼び出すことはありませんnext()けどreturn next()。違いはなんですか?
KansaiRobot

おかげで優しい言葉のためにたくさんの友人...私たちはnext()私たちが次のミドルウェアが呼び出さたいので、私は考えていないnext()return next()、任意の違いを作る必要があります!それでも、コードが何であるかによって異なります...
Vaibhav Bacchav

7

ミドルウェアは、入力/ソースが出力を生成した後、途中で実行される関数であり、最終出力になるか、サイクルが完了するまで次のミドルウェアで使用されます。

これは、組み立てラインを通過する製品のようなものであり、完了するか、評価されるか、拒否されるまで、移動するにつれて修正されます。

ミドルウェアは、何らかの値が機能することを期待し(つまり、パラメーター値)、いくつかのロジックに基づいて、ミドルウェアは次のミドルウェアを呼び出すか呼び出さないか、クライアントに応答を返します。

それでもミドルウェアの概念を理解できない場合は、DecoratorまたはChain of commandパターンに似ています。


5

ミドルウェアは、ユーザー定義ハンドラーが呼び出される前にExpress jsルーティングレイヤーによって呼び出されるチェーン関数のサブセットです。ミドルウェア機能は、要求オブジェクトと応答オブジェクトへのフルアクセスがあり、それらのいずれかを変更できます。

ミドルウェアチェーンは常に定義された順序で呼び出されるため、特定のミドルウェアの動作を正確に把握することが重要です。
ミドルウェア関数が終了すると、次の引数を関数として呼び出すことにより、チェーン内の次の関数を呼び出します。
完全なチェーンが実行された後、ユーザー要求ハンドラーが呼び出されます。


1

物事をシンプルにしてください!

注:答えはExpressJS組み込みミドルウェアのケースに関連していますが、ミドルウェアの定義と使用例は異なります。

私の観点から見ると、ミドルウェアはユーティリティまたはヘルパー機能として機能しますが、app.use('path', /* define or use builtin middleware */)クライアントの各HTTPリクエストに必要な非常に一般的なタスクを実行するためのコードを記述したくないものを使用することで、ミドルウェアのアクティブ化と使用は完全にオプションですほとんどのアプリケーションで非常に一般的なCookie、CSRFトークンなどの処理のように、ミドルウェアは、いくつかのスタック、シーケンス、または操作の順序でクライアントのHTTPリクエストごとにこれらすべてを実行するのに役立ち、プロセスの結果を次のように提供します。クライアント要求の単一ユニット

例:

クライアントの要求を受け入れ、その要求に応じてクライアントに応答を返すことは、Webサーバーテクノロジーの性質です。

「Hello、world!」だけで応答を提供していると想像してみてください。WebサーバーのルートURIへのGET HTTPリクエストのテキストは非常に単純なシナリオであり、他に何も必要ありませんが、代わりに現在ログインしているユーザーを確認してから「Hello、Username!」で応答する場合 この場合、通常以上のものが必要です。すべてのクライアント要求メタデータを処理し、クライアント要求から取得した識別情報を提供するミドルウェアが必要です。その情報に従って、現在のユーザーを一意に識別でき、彼に応答することができます。 /彼女といくつかの関連データ。

それが誰かを助けることを願っています!


-1

非常に基本的な用語で、このように説明したい場合は、traversymedia youtubeチャンネルエクスプレスクラッシュコースからこれを学びます。
わかりましたので、ミドルウェアは、このようにルートを呼び出した後に実行される関数です。

var logger = function(req, res, next){
   console.log('logging...');
   next();
}

app.use(logger);

このロガー関数は、ページを更新するたびに実行されます。つまり、ページに操作api呼び出しがレンダリングされた後、必要なことを何でも書き込むことができ、基本的にすべてをリセットします。このミドルウェアをミドルウェアのルート機能の順序の前に置くと、ミドルウェアが非常に重要になるか、機能しなくなります。

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