NestJS Overview - Interceptors
Intercepterは、
intercepterはAOP技術の中でいくつかの感心させる有用な能力を持っている.メソッド実行前後付加論理バインド 変換関数が返す結果 変換関数放出の異常 拡張基本関数挙動 特定の条件に従って関数 を完全に上書きする.
各インタフェースは、
この方法は、
例えば、
最初の柚子ボックスを使用して表示されるインタフェースは、ユーザーのインタラクションを記録することです.次の
インタフェースは、他のコントローラ、プローブ、およびガードのように、作成者に依存性を注入することができる.
インタフェースを設定するには、
グローバルインタフェースを設定するには、ネストされたアプリケーションインタフェースから
ライブラリ固有のレスポンスポリシーを使用すると、レスポンスマッピング機能が機能しない可能性があります.
上記のコードの作成後、要求が
もう1つの興味深い例は、
Handlerを呼び出すよりも、他の値を返す場合があります.たとえば、応答時間を短縮するためにキャッシュが実装される場合があります.次は簡単なキャッシュインタフェースです.このパラメータがキャッシュされている場合、キャッシュの値が呼び出されます.そうでない場合、Handlerが呼び出されます.
RxJS演算子を用いてストリームを操作する可能性は我々に多くの能力を与えた.他の普通の柚子箱を考えてみましょう.ルーティングリクエストのタイムアウトを処理したいと仮定します.エンドポイントが特定の時間内に何も返されない場合は、リクエストを終了し、エラー応答を返します.この機能は、次のコードで実現できます.
Intercepterを利用して多くのコード重複を解消できると思います.実際のルーティングハンドルメソッドを呼び出す前と後に、追加のタイムアウトなどを設定することもでき、驚きましたが、まだ使ったことがないので、どれほど強力な機能なのか想像できません.
最も重要なのは、RxJSの
@Injectable()
データレコーダに適用されるクラスである.インタフェースは、NestInterceptor
インタフェースを実装する必要があります.intercepterはAOP技術の中でいくつかの感心させる有用な能力を持っている.
Basics
各インタフェースは、
intercept
方法を実装しなければならない.この方法は2つの因子を受け入れる.第1因子はExecutionContext
例であり、第2因子はCallHandler
例である.Execution context
ArgumentsHost
ExecutionContext
を継承することによって、さまざまな現在の実行コンテキストに関する有用な情報を提供するために、いくつかの新しいヘルプメソッドも追加されます.詳細については、以下の文書で説明する.Call handler
CallHandler
インタフェースは、インタフェース内部の必要に応じてルーティングハンドルメソッドを呼び出すためのhandle()
メソッド実装を必要とする.intercept()
メソッド内でhandle()
メソッドを個別に呼び出さないと、ルーティングハンドルはまったく実行されません.この方法は、
intercept()
の方法により、要求/応答ストリームを効率的にパッケージする.その結果、カスタムロジックをルーティングハンドルの前後のすべての場所に追加できます.handle()
メソッド呼び出しの前にロジックを追加することは明確ですが、呼び出し後のロジックはどのように処理すればいいのでしょうか.handle()
メソッドの戻り値はObservable
であるため、必要に応じてRxJS演算子を使用して応答を操作することができる.AOPの用語を用いる場合,ルーティングハンドラの呼び出しをPointcut(他の論理を挿入する場所を示す)と呼ぶ.例えば、
POST /cats
からの要求があると仮定する.要求は、CatsController
の内部に定義されたcreate()
Handlerにあるべきである.handle()
メソッドを呼び出さないインタフェースが途中で呼び出されると、create()
プロセッサがトリガーされます.その後、Observable
を介して応答ストリームが受信されると、ストリーム上で追加の演算が実行された後、最後の結果は呼び出し元に返される.Aspect Interception
最初の柚子ボックスを使用して表示されるインタフェースは、ユーザーのインタラクションを記録することです.次の
LoggingInterceptor
を参照してください.import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
NestInterceptor<T, R>
はGenericインターフェースであり、T
はObservable<T>
のタイプであり、R
はObservable<R>
のタイプである.インタフェースは、他のコントローラ、プローブ、およびガードのように、作成者に依存性を注入することができる.
handle()
メソッドはRxJS Observable
を返すので、複数の演算子を使用してストリームを操作することができる.上記の例では、tap()
演算子を使用して、観測可能なストリームのエレガント/例外の終了時に匿名記録関数を呼び出すが、そうでなければ応答周期には関与しない.Binding Interceptors
インタフェースを設定するには、
@UseInterceptors()
変換器を使用します.パイプやガードのように、intercepterはコントローラ/メソッド/グローバルスキャンで使用できます.@UseInterceptors(LoggingInterceptor)
export class CatsController {}
上記のコードにより、CatsController
のすべてのルーティングハンドルがLoggingInterceptor
を使用します.Get /cats
個のエンドポイントのリクエストがある場合、以下の結果が出力されます.Before...
After... 1ms
上記のコードでは、LoggingInterceptor
の例ではなく、ネストされた依存注入機能が使用される.もちろん、インスタンスをスキップすることもできます.@UseInterceptors(new LoggingInterceptor())
export class CatsController {}
メソッドスキャンにのみ適用したい場合は、メソッドレベルでアクセサリーを適用するだけです.グローバルインタフェースを設定するには、ネストされたアプリケーションインタフェースから
useGlobalInterceptors()
メソッドを呼び出すことができます.const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
グローバルインタフェースは、アプリケーションのグローバルで使用され、すべてのコントローラのすべてのルーティングハンドルで実行されます.依存注入が必要な場合は、次の方法を任意のモジュールで使用できます.import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
Response mapping
handle()
がObservable
を返したのは私たちが知っている事実です.ストリームはルーティングハンドルから返される値を含むので、RxJSのmap()
演算子で内容を変更できます.ライブラリ固有のレスポンスポリシーを使用すると、レスポンスマッピング機能が機能しない可能性があります.
TransformInterceptor
を作ってみます.この受信機は、各応答を明確な方法に変更します.RxJSのmap
演算子を使用して、新しく作成されたオブジェクトのdata
に応答オブジェクトを入れ、クライアントに返します.import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response<T> {
data: T;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
return next.handle().pipe(map(data => ({ data })));
}
}
Nest Intercepterはsync/async intercept()
メソッドをサポートします.もしあなたが望むなら、交換すればいいです.上記のコードの作成後、要求が
GET /cats
エンドポイントに入ると、応答は以下のように表示される(ルーティングプロセッサが空の配列を返すと仮定する){
"data": []
}
Intercepterがアプリケーション全体で再利用可能なソリューションを作成することは、非常に価値があります.たとえば、null
のすべての値を空の文字列値''
に変更する必要があるとします.1行のコードがインタフェースをグローバルにバインドする場合は、すべてのプロセッサに適用されます.import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class ExcludeNullInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(map(value => value === null ? '' : value ));
}
}
Exception mapping
もう1つの興味深い例は、
RxJS
のcatchError
演算子によって投げ出された例外である.import {
Injectable,
NestInterceptor,
ExecutionContext,
BadGatewayException,
CallHandler,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(
catchError(err => throwError(new BadGatewayException())),
);
}
}
Stream overriding
Handlerを呼び出すよりも、他の値を返す場合があります.たとえば、応答時間を短縮するためにキャッシュが実装される場合があります.次は簡単なキャッシュインタフェースです.このパラメータがキャッシュされている場合、キャッシュの値が呼び出されます.そうでない場合、Handlerが呼び出されます.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, of } from 'rxjs';
@Injectable()
export class CacheInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const isCached = true;
if (isCached) {
return of([]);
}
return next.handle();
}
}
上記のCacheInterceptor
は、ハードコーディングのisCached
変数を有し、ハードコーディングの戻り値は[]
である.ここで、RxJS of
演算子によって新しいストリームが返されるため、ルーティングハンドルはまったく呼び出されません.アプリケーションのエンドポイントとしてキャッシュインタフェースを要求する人がいる場合、応答はすぐに返されます.一般的な解決策を策定するために、新しいベンチマークカウンタを作成し、内部でReflector
を利用することができる.More operators
RxJS演算子を用いてストリームを操作する可能性は我々に多くの能力を与えた.他の普通の柚子箱を考えてみましょう.ルーティングリクエストのタイムアウトを処理したいと仮定します.エンドポイントが特定の時間内に何も返されない場合は、リクエストを終了し、エラー応答を返します.この機能は、次のコードで実現できます.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from '@nestjs/common';
import { Observable, throwError, TimeoutError } from 'rxjs';
import { catchError, timeout } from 'rxjs/operators';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
timeout(5000),
catchError(err => {
if (err instanceof TimeoutError) {
return throwError(new RequestTimeoutException());
}
return throwError(err);
}),
);
};
};
5秒後、応答処理は停止します.RequestTimeoutException
を投げる前に、より多くのカスタムロジックを入れることもできます.に感銘を与える
Intercepterを利用して多くのコード重複を解消できると思います.実際のルーティングハンドルメソッドを呼び出す前と後に、追加のタイムアウトなどを設定することもでき、驚きましたが、まだ使ったことがないので、どれほど強力な機能なのか想像できません.
最も重要なのは、RxJSの
Observable
と演算子についてよく知らないため、理解には一定の限界があるようだ.これらの友达は勉强してから见ると、もっとよく理解して、もっとよく使うことができます.Reference
この問題について(NestJS Overview - Interceptors), 我々は、より多くの情報をここで見つけました https://velog.io/@mskwon/NestJS-Overview-Interceptorsテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol