過去7、8年の間にNodeの開発に携わってきた人であれば、どこかでExpressを使ってWebサーバーを構築したことがあるでしょう。 ライブラリを使用せずにNodeでサーバを作成することはできますが、すぐに多くのことができるわけではありませんし、機能を追加するのは非常に面倒です。 Expressは、ミニマリストで「意見を持たない」サーバライブラリであり、Nodeでウェブアプリを構築する際のデファクトスタンダードとなっています。 Expressを理解するには、Expressミドルウェアを理解する必要があります。
Expressミドルウェアとは
ミドルウェアとは、文字通り、ソフトウェアのある層と別の層の間に入れるものを意味します。 Expressのミドルウェアは、Expressサーバーへのリクエストのライフサイクル中に実行される機能です。 各ミドルウェアは、接続されている各ルート(またはパス)の HTTP request
response
へのアクセス権を持っています。 実際、Express自体がミドルウェアの機能で全て構成されています。 さらに、ミドルウェアはHTTPリクエストを終了させたり、next
を使用して別のミドルウェア関数に渡したりすることができます(詳細は近日中に説明します)。
この記事では、ミドルウェアとは何か、なぜそれを使用するのか、既存のExpressミドルウェアを使用する方法、Express用に独自のミドルウェアを書く方法について説明します。
Requirements to Write Express Middleware
Expressミドルウェアを作成、使用、テストするためには、いくつかのものをインストールする必要があります。 まず、NodeとNPMが必要になります。 これらがインストールされていることを確認するには、次のように実行します。
npm -v && node -v
インストールされているNodeとNPMのバージョンが表示されるはずです。 もしエラーが出たら、Nodeをインストールする必要があります。 私はこの記事の時点で両方の最新バージョンであるNode 10.9.0とNPM 6.4.1を使用していますが、すべての例はNodeバージョン8+とNPMバージョン5+で動作するはずです。
また、Expressバージョン4.xを使用します。
また、Expressのバージョン4.xを使用しますが、これはバージョン3.xから4.xに大きな変更が加えられたためです。
また、GET
以外のHTTP動詞を使用したルートをテストするために、Postmanをインストールしておくと便利です。
Expressミドルウェア。 基本編
まず始めに、Expressに組み込まれているミドルウェアのうち、最も基本的なものを使用します。 これにより、ミドルウェアがどのように使用されているか、Expressのミドルウェアがどのように構成されているかを確認することができます。
新規プロジェクトを作成し、npm init
それを…
npm initnpm install express --save
server.js
を作成し、以下のコードを貼り付けます。
const express = require('express');const app = express();app.get('/', (req, res, next) => { res.send('Welcome Home');});app.listen(3000);
node server.js
http://localhost:3000
にアクセスすると、ブラウザに「Welcome Home」と表示されるはずです。
app.get()
req
res
next
であることがわかります。 これらは、入力されたリクエスト、書き込まれるレスポンス、そして、現在のミドルウェアが終了したら次のミドルウェア関数に呼び出しを渡すためのメソッドです。 この例では、レスポンスが送信されると、この関数は終了します。
異なるタイプのミドルウェアの例をもう少し見てみましょう。
Express Request Logging Middleware Example
Expressでは、ミドルウェアを「グローバル」なミドルウェアとして設定することができます(入ってくるリクエストごとに呼び出される)。
server.js
の内容を以下のように変更します。
const express = require('express');const app = express();app.use((req, res, next) => { console.log(req); next();});app.get('/', (req, res, next) => { res.send('Welcome Home');});app.listen(3000);
今回、http://localhost:3000
にアクセスすると、ブラウザ ウィンドウには同じものが表示されますが、コンソール ウィンドウに戻ると、受信するリクエスト オブジェクトの出力が表示されます。
ミドルウェアはリクエスト オブジェクトをログアウトしてから next()
を呼び出します。 パイプラインの次のミドルウェアは、ルートURLへのgetリクエストを処理し、テキストレスポンスを送り返します。
Restrict Express Request Content Typeの例
すべての呼び出しに対してミドルウェアを実行することに加えて、特定の呼び出しに対してのみミドルウェアを実行するように指定することもできます。
server.js
ファイルを再度以下のように変更します:
const express = require('express');const app = express();const requireJsonContent = () => { return (req, res, next) => { if (req.headers !== 'application/json') { res.status(400).send('Server requires application/json') } else { next() } }}app.get('/', (req, res, next) => { res.send('Welcome Home');});app.post('/', requireJsonContent(), (req, res, next) => { res.send('You sent JSON');})app.listen(3000);
今度は、実行してサーバーを起動します。
node server.js
これをテストするために、Postmanを開き、http://localhost:3000
への投稿リクエストを作成します。 何もヘッダーを設定せず、リクエストを実行します。
ここでもう一度、Content-Type
application/json
の値を追加して、リクエストを実行します。
この app.post()
requireJsonContent()
Content-Type
application/json
に設定されていることを確認します。 チェックに合格しない場合は、エラーレスポンスが送信されます。
サードパーティ製の Express ミドルウェア
ここまでいくつかのカスタム ミドルウェアを構築してきましたが、通常行いたいことを行うためのパッケージはすでにたくさんあります。 実際には、app.get()
app.post()
のミドルウェア関数を使用して、シンプルなルーティング ミドルウェア ライブラリを使用しています。
OktaにはOIDCセキュリティのためのExpressミドルウェアがあり、サードパーティのミドルウェアライブラリを使用したデモをお見せします。
なぜOkta for Express Applicationsなのか
Oktaでは、アイデンティティ管理をこれまでよりもずっと簡単に、より安全に、よりスケーラブルにすることを目標としています。 Oktaは、開発者がユーザーアカウントやユーザーアカウントデータを作成、編集、安全に保管し、1つまたは複数のアプリケーションに接続することができるクラウドサービスです。
- ユーザーの認証と認可
- ユーザーに関するデータの保存
- パスワードベースおよびソーシャルログインの実行
- 多要素認証によるアプリケーションのセキュリティ確保
- その他多数。
OktaのOIDC Expressミドルウェア
OktaのOIDC Express用ミドルウェアをインストールするには、以下を実行します。
npm install @okta/[email protected] --save
次に、server.js
ファイルで、Okta が Okta アプリケーションへの接続方法を知ることができるように、いくつかの構成オプションを持つミドルウェアのインスタンスを作成します。
const oidc = new ExpressOIDC({ issuer: 'https://{yourOktaDomain}/oauth2/default', client_id: '{yourClientId}', client_secret: '{yourClientSecret}', redirect_uri: 'http://localhost:3000/authorization-code/callback', scope: 'openid profile'});
また、デフォルトのルーターではなく、OIDCミドルウェアのルーターを使用するようにExpressに指示する必要があります。
app.use(oidc.router);
あとは、他のミドルウェアと同じように使用します。
app.get('/protected', oidc.ensureAuthenticated(), (req, res) => { res.send('Top Secret');});
oidc.ensureAuthenticated()
関数はOktaライブラリのミドルウェアです。 現在のユーザーがログインしているかどうかを確認する関数を実行します。 もしログインしていれば、next()
app.get()
関数にリクエストの処理を続けさせます。
ミドルウェアの順番が重要
Expressがリクエストを受信すると、リクエストにマッチした各ミドルウェアは、初期化された順番に、終了するアクション(レスポンスの送信など)があるまで実行されます。
そのため、エラーが発生した場合、エラーを処理することを目的としたすべてのミドルウェアが、いずれかのミドルウェアがnext()
の関数呼び出しを行わないまで、順番に呼び出されます。
Expressミドルウェアのエラー処理
Expressには、ミドルウェアのパイプラインの最後に挿入されるデフォルトのエラーハンドラが組み込まれており、パイプライン内で発生した未処理のエラーを処理します。 そのシグネチャは、リクエスト、レスポンス、ネクストの標準パラメータにエラーパラメータを追加します。 基本的な署名は次のようになります。
app.use((err, req, res, next) => { // middleware functionality here})
エラー処理を行うミドルウェアを呼び出すには、次のようにnext()
にエラーを渡すだけです。
app.get('/my-other-thing', (req, res, next) => { next(new Error('I am passing you an error!'));});app.use((err, req, res, next) => { console.log(err); if(!res.headersSent){ res.status(500).send(err.message); }});
この場合、パイプラインの最後にあるエラー処理ミドルウェアがエラーを処理します。 また、res.headersSent
プロパティをチェックしたことに気づくかもしれません。 これは、レスポンスがすでにクライアントにヘッダーを送信しているかどうかを確認するだけです。 もし送信されていなければ、500 HTTPステータスとエラーメッセージをクライアントに送信します。 エラー処理のミドルウェアを連鎖させることもできます。 これは、異なるタイプのエラーを異なる方法で処理するための一般的な方法です。 たとえば、
app.get('/nonexistant', (req, res, next) => { let err = new Error('I couldn\'t find it.'); err.httpStatusCode = 404; next(err);});app.get('/problematic', (req, res, next) => { let err = new Error('I\'m sorry, you can\'t do that, Dave.'); err.httpStatusCode = 304; next(err);});// handles not found errorsapp.use((err, req, res, next) => { if (err.httpStatusCode === 404) { res.status(400).render('NotFound'); } next(err);});// handles unauthorized errorsapp.use((err, req, res, next) => { if(err.httpStatusCode === 304){ res.status(304).render('Unauthorized'); } next(err);})// catch allapp.use((err, req, res, next) => { console.log(err); if (!res.headersSent) { res.status(err.httpStatusCode || 500).render('UnknownError'); }});
このケースでは、ミドルウェアは404(not found)エラーがスローされたかどうかをチェックします。 もしそうであれば、「NotFound」テンプレートページをレンダリングし、そのエラーをミドルウェアの次の項目に渡します。 次のミドルウェアでは、304(authorized)エラーがスローされたかどうかを確認します。 エラーが発生した場合は、「Unauthorized」ページをレンダリングし、そのエラーをパイプラインの次のミドルウェアに渡します。
Learn More About Express Middleware
Okta OIDC ミドルウェアの設定に関する詳細な手順については、ExpressJS Quickstart を参照してください。
また、公式にサポートされているExpressミドルウェアのリストがこのGitHub repoにありますので、試してみたり、詳しく調べたりすることができます
最後に、Oktaの使い方についてもっと知りたいという方には、アプリケーションにもっと多くのユーザー管理機能を実装するためのOkta Node SDKがあります
。