Перахопнікі
Перахопнік - гэта клас, анатаваны дэкаратарам @Injectable()
і рэалізуе інтэрфейс NestInterceptor
.
Перахопнікі маюць набор карысных магчымасцей, якія натхнёны тэхнікай аспектна-арыентаванага праграмавання (AOP). Яны дазваляюць:
- звязаць дадатковую логіку да / пасля выканання метаду
- пераўтварыць вынік, вернуты функцыяй
- ператварыць выключэнне, выкінутае з функцыі
- пашырыць паводзіны асноўных функцый
- цалкам перавызначыць функцыю ў залежнасці ад пэўных умоў (напрыклад, для мэт кэшавання)
Асновы
Кожн ы перахопнік рэалізуе метад intercept()
, які прымае два аргументы. Першы - асобнік ExecutionContext
(дакладна такі ж аб'ект, што і для guards ). ExecutionContext
успадкоўваецца ад ArgumentsHost
. Мы бачылі ArgumentsHost
раней у раздзеле фільтраў выключэнняў. Там мы ўбачылі, што гэта абалонка вакол аргументаў, якія былі перададзены зыходнаму апрацоўшчыку, і змяшчае розныя масівы аргументаў у залежнасці ад тыпу прыкладання. Вы можаце вярнуцца да фільтраў выключэнняў, каб даведацца больш па гэтай тэме.
Кантэкст выканання
Пашыраючы ArgumentsHost
, ExecutionContext
таксама дадае некалькі новых дапаможных метадаў, якія даюць дадатковую інфармацыю аб бягучым працэсе выканання. Гэтыя дэталі могуць быць карыснымі пры стварэнні больш агульных перахопнікаў, якія могуць працаваць з шырокім наборам кантролераў, метадаў і кантэкстаў выканання. Даведайцеся больш пра ExecutionContext
тут .
Апрацоўшчык выклікаў
Другі аргумент - CallHandler
. Інтэрфейс CallHandler
рэалізуе метад handle()
, які вы можаце выкарыстоўваць для выкліку метаду апрацоўшчыка маршруту ў нейкі момант вашага перахопніка. Калі вы не выклікаеце метад handle()
у вашай рэалізацыі метаду intercept()
, метад апрацоўшчыка маршруту не будзе выкананы наогул.
Такі падыход азначае, што метад intercept()
эфектыўна абгортвае паток запыту/адказу. У выніку вы можаце рэалізаваць карыстальніцкую логіку як да, так і пасля выканання канчатковага апрацоўшчыка маршруту. Зразумела, што вы можаце напісаць код у метадзе intercept()
, які выконваецца перад выклікам handle()
, але як вы паўплываеце на тое, што адбудзецца пасля? Паколькі метад handle()
вяртае Observable
, мы можам выкарыстоўваць магутныя аператары RxJS для далейшага маніпулявання адказам. Выкарыстоўваючы тэрміналогію аспектна-арыентаванага праграмавання, выклік апрацоўшчыка маршруту (напрыклад, выклік handle()
) называецца Pointcut , паказваючы, што гэта кропка, у якую ўстаўляецца наша дадатковая логіка.
Разгледзім, напрыклад, уваходны запыт POST /cats
. Гэты запыт прызначаны для апрацоўшчыка create()
, вызначанага ўнутры CatsController
. Калі дзе-небудзь па шляху выклікаецца перахопнік, які не выклікае метад handle()
, метад create()
не будзе выкананы. Пасля выкліку handle()
(і яго Observable
быў вернуты), апрацоўшчык create()
будзе запушчаны. І як толькі паток адказу атрыманы праз Observable
, над патокам можна выканаць дадатковыя аперацыі, і канчатковы вынік будзе вернуты абаненту.
Аспектны перахоп
Першы варыянт выкарыстання, які мы разгледзім, - гэта выкарыстанне перахопніка для рэгістрацыі ўзаемадзеяння з карыстальнікам (напрыклад, захоўвання выклікаў карыстальнікаў, асінхроннай адпраўкі падзей або вылічэння меткі часу). Ніжэй мы паказваем просты LoggingInterceptor
:
@@filename(logging.interceptor)
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`)),
);
}
}
@@switch
import { Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor {
intercept(context, next) {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
NestInterceptor<T, R>
- гэта агульны інтэрфейс, у якім T
паказвае тып Observable<T>
(які падтрымлівае паток адказу), а R
- тып значэння, абгорнутага Observable<R>
. ::::::папярэджанне Перахопнікі апавяшчэнняў , такія як кантролеры, правайдэры, ахоўнікі і гэтак далей, могуць уводзіць залежнасці праз свой constructor
. :::
Паколькі handle()
вяртае RxJS Observable
, у нас ёсць шырокі выбар аператараў, якія мы можам выкарыстоўваць для маніпулявання патокам. У прыведзеным вышэй прыкладзе мы выкарысталі аператар tap()
, які выклікае нашу ананімную функцыю вядзення часопіса пасля лагоднага або выключнага завяршэння назіранага патоку, але ў іншым выпадку не перашкаджае цыклу адказу.
Перахопнікі прывязкі
Каб наладзіць перахопнік, мы выкарыстоўваем дэкаратар @UseInterceptors()
, імпартаваны з пакета @nestjs/common
. Як трубы і ахоўнікі , перахопнікі могуць быць з вобласцю кантролера, метаду або глабальнай вобласцю.
@@filename(cats.controller)
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
@UseInterceptors()
імпартаваны з пакета @nestjs/common
. :::З дапамогай прыведзенай вышэй канструкцыі кожны апрацоўшчык маршрутаў, вызначаны ў CatsController
будзе выкарыстоўваць LoggingInterceptor
. Калі нехта выклікае канчатковую кропку GET /cats
, вы ўбачыце наступны вывад у вашым стандартным вывадзе:
Before...
After... 1ms
Звярніце ўвагу, што мы перадалі клас LoggingInterceptor
(замест асобніка), пакінуўшы адказнасць за стварэнне асобніка фрэймворку і дазволіўшы ін'екцыю залежнасці. Як і ў выпадку з каналамі, ахоўнікамі і фільтрамі выключэнняў, мы таксама можам перадаць асобнік на месцы:
@@filename(cats.controller)
@UseInterceptors(new LoggingInterceptor())
export class CatsController {}
Як ужо згадвалася, канструкцыя вышэй далучае перахопнік да кожнага апрацоўшчыка, аб'яўленага гэтым кантролерам. Калі мы хочам абмежаваць вобласць дзеяння перахопніка адным метадам, мы проста ўжываем дэкаратар на ўзроўні метаду .
Каб наладзіць глабальны перахопнік, мы выкарыстоўваем метад useGlobalInterceptors()
асобніка прыкладання Nest:
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
Глабальныя перахопнікі выкарыстоўваюцца ва ўсім дадатку, для кожнага кантролера і кожнага апрацоўшчыка маршрутаў. З пункту гледжання ўвядзення залежнасцей, глабальныя перахопнікі, зарэгістраваныя па-за межамі любога модуля (з дапамогай useGlobalInterceptors()
, як у прыкладзе вышэй), не могуць уводзіць залежнасці, паколькі гэта робіцца па-за кантэкстам любога модуля. Каб вырашыць гэтую праблему, вы можаце наладзіць перахопнік непасрэдна з любога модуля, выкарыстоўваючы наступную канструкцыю:
@@filename(app.module)
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}