NestJS公式ドキュメント翻訳
原文
Middleware| NestJS - A progressive Node.js web framework
ミドルウェア
ミドルウェアはルートハンドラの前に呼び出される関数です。ミドルウェア関数はリクエストオブジェクトとレスポンスオブジェクト、およびアプリケーションのリクエスト/レスポンスサイクルのnext()
ミドルウェア関数にアクセスできます。next
ミドルウェア関数は、一般にnext
という名前の変数によって示されます。
Nestのミドルウェアは、デフォルトではexpressのミドルウェアと同等です。Express公式ドキュメントによると、以下のようにミドルウェアの機能について説明しています。
ミドルウェア機能では、次のタスクを実行できます。
・コードの実行
・リクエストおよびレスポンスオブジェクトに変更を加える
・リクエストとレスポンスのサイクルを終了する
・スタック内の次のミドルウェア関数を呼び出す
・現在のミドルウェア関数でリクエストとレスポンスのサイクルを終了しない場合、次のミドルウェア関数に制御を渡すためにnext()
を呼び出す必要があります。そうしなければリクエストがハングします。
カスタムNestミドルウェアは、関数または@Injectable()
デコレータを持つクラスのいずれかに実装します。クラスにはNestMiddleware
インターフェースを実装する必要がありますが、関数に特別な要件はありません。クラスメソッドを使用して簡単なミドルウェア機能を実装することから始めましょう。
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('Request...');
next();
}
}
依存性の注入
Nestミドルウェアは依存性の注入を完全にサポートしています。プロバイダやコントローラと同様に、同じモジュール内で利用可能な依存関係を注入できます。これもconstructor
を介して行われます。
ミドルウェアの適用
@Module()
デコレータにはミドルウェアを記述する場所がありません。代わりに、モジュールクラスのconfigure()
メソッドを使用してセットアップします。ミドルウェアを含むモジュールは、NestModule
インターフェイスを実装する必要があります。 AppModule
レベルでLoggerMiddleware
をセットアップしましょう。
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
上記の例では、以前にCatsController
内で定義された/cats
ルートハンドラー用のLoggerMiddlewareをセットアップしました。ミドルウェアの設定時に、ルートpath
とリクエストmethod
を含むオブジェクトをforRoutes()
メソッドに渡すことにより、特定のリクエストメソッドに対するミドルウェアをにさらに制限することもできます。以下の例では、RequestMethod
をインポートして、目的のリクエストメソッド型を参照していることに注意してください。
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET });
}
}
configure()
メソッドはasync/await
を使用して非同期にすることができます(たとえば、configure()
内で非同期操作の完了をawait
することができます)。
ルートワイルドカード
パターンベースのルートもサポートされています。たとえば、アスタリスクはワイルドカードとして使用され、任意の文字の組み合わせに一致します。
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
'ab*cd'
ルートパスはabcd
、ab_cd
、abecd
などに一致します。文字?
、+
、*
、および()
はルートパスで使用でき、正規表現のサブセットです。ハイフン(-
)とドット(.
)は文字列ベースのパスによって文字通り解釈されます。
ミドルウェアコンシューマ
MiddlewareConsumer
はヘルパークラスです。ミドルウェアを管理するためのいくつかの組み込みメソッドを提供します。それらのすべては、fluent styleで簡単に連鎖することができます。forRoutes()
メソッドは単一の文字列、複数の文字列、RouteInfo
オブジェクト、コントローラクラス、さらには複数のコントローラクラスを受け取ることができます。ほとんどの場合は、おそらくコンマで区切られたコントローラのリストを渡すだけでしょう。以下は、単一のコントローラを使用した例です。
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller.ts';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
apply()
メソッドは単一のミドルウェアまたは複数の引数を取り、複数のミドルウェアを指定できます。
多くの場合、特定のルートをミドルウェアの適用外にしたい場合があります。(関数ミドルウェアを使用するのではなく、これまで行ってきたように)クラスでミドルウェアを定義する場合、exclude()
メソッドを使用して特定のルートを簡単に除外できます。以下に示すように、このメソッドは除外するpath
とmethod
を識別する1つ以上のオブジェクトを取ります。
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST }
)
.forRoutes(CatsController);
上記の例では、LoggerMiddleware
はexclude()
メソッドに渡される2つを除くCatsController
内で定義されたすべてのルートにバインドされます。exclude()
メソッドは関数ミドルウェア(クラスではなく関数で定義されたミドルウェア。詳細は以下を参照)では機能しないことに注意してください。さらに、このメソッドはより一般的なルート(ワイルドカードなど)からのパスを除外しません。そのレベルの制御が必要な場合は、パスによって制限するロジックをミドルウェアに直接持たせましょう。たとえば、リクエストURLにアクセスしてミドルウェアロジックを条件付きで適用する必要があります。
関数ミドルウェア
私たちが使用しているLoggerMiddleware
クラスは非常に単純です。メンバー、追加のメソッド、依存関係はありません。クラスではなく単純な関数で定義できないのはなぜでしょうか?実は、関数化することができます。このタイプのミドルウェアは、関数ミドルウェアと呼ばれます。ロガーミドルウェアをクラスベースから関数ミドルウェアに変換して、その違いを説明します。
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
AppModule
内で使用します。
consumer
.apply(logger)
.forRoutes(CatsController);
ミドルウェアが依存関係を必要としない場合は、よりシンプルな関数ミドルウェアを代わりに使用することを検討してください。
マルチミドルウェア
前述のように、順次実行される複数のミドルウェアをバインドするにはapply()
メソッド内にカンマ区切りのリストを指定するだけです。
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
グローバルミドルウェア
ミドルウェアをすべての登録済みルートに一度にバインドする場合、INestApplication
インスタンスによって提供されるuse()
メソッドを使用できます。
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);