【NodeでWebアプリ】(5) Expressミドルウェア

express2-2.png

5. Expressミドルウェア

Expressミドルウェアは、Express.JSの根幹的な機構で、Express.JSを使いこなすためには理解しておきたいしくみです。Expressジェネレータが作成するスケルトンではミドルウェアが各所で使われていますし、サードパーティのミドルウェアをNPMで簡単に組み込むこともできます。また、プログラマが独自のミドルウェアを追加してシンプルに機能を拡張することもできます。

5-1. ミドルウェアの概念

ミドルウェアの概念を理解するために、Express.JSの基本的な処理の流れをおさらいします。Express.JSは、ブラウザからHTTPリクエストを受けとり、何らかの処理をしてレスポンスを返します。これをリクエストレスポンスサイクルといいます。

概念的には、ミドルウェアはこの流れの中間に位置する処理のまとまりです。例えて言うなら、コーヒーショップのドライブスルーで入り口から出口までの間に、「注文をとる」「コーヒーを渡す」「代金を受け取る」というミドルウェアがあるようなイメージです。これらの処理は、注文するメニューによらず、どのようなリクエストの場合も共通する処理の流れです。

Express.JSでは、複数のURLパスへのリクエストに対して行う処理をミドルウェアというまとまりにして、リクエストレスポンスサイクルの中間に配置することができます。ミドルウェアはいくつでも登録・実行させることがき、連続したミドルウェアのことをミドルウェアチェーンと呼びます。

5-2. ミドルウェアの実装

実質的には、ミドルウェアは単純にJavaScriptのfunctionです。app.use( )などの関数を使ってfunctionをマウントします。

実装のイメージを伝えるために仮想のコードを書きます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
app.js
*/

var app = express();


app.use(function(...){

next();
});

// /adminのリクエストにマウント
app.use("/admin", function(...){

next();
});

同じことを、以下のように書くこともできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
app.js
*/

var m1 = function(...){

next();
};
var m2 = function(...){

next();
};

var app = express();
app.use(m1);
app.use("/admin",m2);

また、.use() の代わりに.get().put()というインターフェースが用意されており、リクエストメソッドをGETやPUTに限定してミドルウェアをマウントすることができます。

1
2
3
4
5
6
7
8
9
10
11
12

/* GET /admin にマウント */
app.get("/admin", function(...){

next();
});

/* PUT /save にマウント */
app.put("/save", function(...){

next();
});

5-3. ミドルウェアの種類

ミドルウェアには以下のタイプがあります。

  • アプリケーション・レベルのミドルウェア
  • ルーター・レベルのミドルウェア
  • Express本体に標準装備されたミドルウェア
  • サード・パーティー・ミドルウェア

5-4. アプリケーション・レベルのミドルウェア

アプリケーション・レベルのミドルウェアはapp = express()appにマウントするミドルウェアです。マウントするfunctionの引数として次の3つが決められています。

引数1 : リクエストオブジェクト (慣習的にreqという変数名)
引数2 : レスポンスオブジェクト (慣習的にresという変数名
引数3 : 次のミドルウェアに処理を渡すコールバック(慣習的にnextという変数名)

1
2
3
4
5
6
var app = express();

app.use(function(req, res, next){
console.log("Hello Middleware")
next();
});

この3つの引数が渡されることには次のような意味があります。

req : どのミドルウェアでも、ブラウザからのリクエスト情報を直接知ることができる。
res : どのミドルウェアでも、ブラウザへのレスポンスを直接操作できる。
next : どのミドルウェアも、次のミドルウェアに処理を渡すときはnext()をコールバックする。

resオブジェクトを直接操作できるので、res.send()などのメソッドを使えば、ミドルウェアの中からリクエストレスポンスサイクルを終了させることができます。以下の例では、エラーのときはres.send()を実行してリクエストレスポンスサイクルを終了し、正常のときはnext()で処理を流しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

var app = express();
...
...
app.use(function(req, res, next){

// URLの改ざんをチェックする処理
var result = myURLCheck(req.url)
if(result.error){
console.log('URL Check error.', req.url);
// resオブジェクトを使ってブラウザにレスポンスを返す
res.send('このURLは無効です');
}else{
// nextを呼んで処理を続行する
next();
}
});

もしも、res.send()next() もどちらも呼ばないコードを書いてしまうと、リクエストに対する処理がそこで行き止まりになってしまい、ブラウザからのリクエストはタイムアウトします。

5-5. ルーター・レベルのミドルウェア

ルーター・レベルのミドルウェアは、router = express.Router()routerにマウントするミドルウェアです。ルーターが複数あるときに個別のルーターに対してミドルウェアを直接マウントすることができます。

Expressジェネレータで生成されるスケルトンで、既にこのタイプのミドルウェアが使われています。 /routes/users.js を開くと以下のようなコードがありますが、router.get()の部分はルーター・レベルのミドルウェアです。

1
2
3
4
5
6
7
8
9
10
11
12
/*
/routes/users.js
*/
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});

module.exports = router;

試しにこのスケルトンに対して、「日曜はこのページを利用できないようにする」という機能をミドルウェアとして追加してみます。(このコードは/routers/users.jsなので「このページ」というのは/users/のページということになります。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
/routes/users.js
*/
var express = require('express');
var router = express.Router();

/* 自作のミドルウェアを追加 */
router.use(function(req, res, next) {
var d = new Date();
if(d.getDay() == 0){
res.send('日曜はこのページを利用できません');
}else{
//日曜以外なら処理を抜ける
next();
}
});

/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});

module.exports = router;

5-6. Express本体に標準装備されたミドルウェア

Express.JS本体にビルトインされているミドルウェアがあります。ここでは個々のミドルウェアの説明を省略します。

  • express.static : スタティックなファイルを配信
  • express.json : json処理
  • express.urlencoded : URLエンコード処理

app.jsのコードを見てみると以下のような記述を見つけることができます。

1
2
3
4
5
6
7
8
9
10
/*
app.js
*/
var express = require('express');
var app = express();
...
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
...
app.use(express.static(path.join(__dirname, 'public')));

このコードをパッと見て気がつきますが、.use( ) のかっこの中がfunction(req,res,next){...}という形になっていません。
これは、express.json()express.urlencoded()が以下のようにfunctionをreturnする構造になっているためで、実質的にはこれまで見てきたミドルウェアの形式と同じことです。

1
2
3
4
return function (req, res, next) {
...
next();
}

5-7. サード・パーティー・ミドルウェア

npmを使ってインストール可能なミドルウェアがたくさん存在します。Expressジェンレレータが生成するスケルトンでも、以下のようなミドルウェアが使われています。

app.jsのコードを見てみると以下のような記述を見つけることができます。

1
2
3
4
5
6
7
8
9
10
11
/*
app.js
*/
var app = express();
...
var cookieParser = require('cookie-parser');
var logger = require('morgan');
...
app.use(logger('dev'));
app.use(cookieParser());
...

この他にも、よく使うサード・パーティー・ミドルウェアには以下のようなものがあります。

まとめ

  • Expressミドルウェアは、Expressのリクエストレスポンスサイクルの中間にfunctionを配置するしくみです。
  • 以下のような書式で記述できます。
1
2
3
4
app.use(function(req, res, next){

next();
});
  • ミドルウェアには以下のようなタイプがあります:

    アプリケーション・レベル
    ルーター・レベル
    Express本体に標準装備
    サード・パーティー

writer | 太田直毅
株式会社OTAシステム開発。フルスタックエンジニア。 仙台市生まれ。大学卒業後に米国系通信会社にエンジニアとして就職。 1998年に独立し、WEBアプリケーションの開発を中心に活動。2児の父。趣味は家族キャンプ。
この記事をシェアする