8、Nest.js 中的攔截器

什么是攔截器(Interceptor)夯辖?

攔截器就是使用 @Injectable 修飾并且實現(xiàn)了 NestInterceptor 接口的類琉预。
在Nest中攔截器是實現(xiàn) AOP 編程的利器。

傳統(tǒng) MVC 應用

Nest 默認將控制器處理程序的返回值解析成 JSON(純字符串不解析)蒿褂,我們?nèi)绾卧?Nest 中實現(xiàn)傳統(tǒng) MVC 程序呢圆米? 即返回一個使用模板引擎渲染的視圖卒暂。

首先選擇一個模板引擎,這里使用的 art-template

chart.png

npm install --save art-template
npm install --save express-art-template

修改我們的入口程序:

src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from 'app.module';
import { HttpExceptionFilter } from 'common/filters/http-exception.filter';
import { ApiParamsValidationPipe } from 'common/pipes/api-params-validation.pipe';
import { static as resource } from 'express';
import * as art from 'express-art-template';

async function bootstrap() {

  const app = await NestFactory.create(AppModule);
  
  // 處理靜態(tài)文件
  app.use('/static', resource('resource'));

  // 指定模板引擎
  app.engine('art', art);

  // 設置模板引擎的配置項
  app.set('view options', {
      debug: process.env.NODE_ENV !== 'production',
      minimize: true,
      rules: [ 
        { test: /<%(#?)((?:==|=#|[=-])?)[ \t]*([\w\W]*?)[ \t]*(-?)%>/ },
        { test: /{%([@#]?)[ \t]*(\/?)([\w\W]*?)[ \t]*%}/ }
     ]
  });
  
  // 設置視圖文件的所在目錄
  app.setBaseViewsDir('resource/views');

  app.useGlobalFilters(new HttpExceptionFilter());
  app.useGlobalPipes(new ApiParamsValidationPipe());

  await app.listen(3000);
}
bootstrap();

目前程序已經(jīng)可以處理 resource 目錄下的靜態(tài)文件了娄帖。
resource 目錄是和 src 目錄平級的也祠,看起來像下面這樣:


image.png

新建一個 home 模塊:

image.png

在控制器中拿到 response 對象然后調(diào)用它的 render 函數(shù)就可以返回渲染后的 html:

src/home/home.controller.ts

import { Controller, Get, Res } from '@nestjs/common';

@Controller()
export class HomeController {

    @Get()
    index(@Res() res) {
    
        return res.render('home/home.art');
    }
}

這樣做太丑陋了,我們的思路是近速,只要控制器返回的是一個 View 類型诈嘿,則渲染視圖,否則使用默認的解析邏輯削葱。

新建一個 View.ts:

src/common/libs/View.ts

export class View {
    
    // 視圖的名稱
    public name: string
    
    // 要渲染的數(shù)據(jù)源
    public data: any

    constructor(name: string, data: any = {}) {
        this.name = name;
        this.data = data;
    }
}

HomeController 中就是很簡單的返回一個 View 的實例:

src/home/home.controller.ts

import { Controller, Get } from '@nestjs/common';
import { View } from 'common/libs/view';

@Controller()
export class HomeController {

    @Get()
    index(): View {
        // 返回首頁的視圖
        return new View('home/home.art');
    }
}

攔截器登場

src/common/Interceptors/view.interceptor.ts

import { ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import * as util from 'util';
import { View } from '../libs/view';

@Injectable()
export class ViewInterceptor implements NestInterceptor {
    intercept(
        context: ExecutionContext,
        call$: Observable<any>,
    ): Observable<any> {
        // 拿到 response 對象
        const response = context.switchToHttp().getResponse();
        
        // 將 render 回調(diào)函數(shù)轉成一個 promisify 然后綁定執(zhí)行的上下文
        const render = util.promisify(response.render.bind(response));
        
        // 請自行了解什么是 Rxjs 
        return call$.pipe(map(async value => {

            if (value instanceof View) {
                // 返回渲染后的 html
                value = await render(value.name, value.data);

            } 

            return value;
            
        }))

    }
}

這里對于一些基礎知識如 promisify奖亚, bind 等不做介紹了。每個攔截器都有 intercept() 方法析砸,這個方法有2個參數(shù)昔字。 第一個是 ExecutionContext 實例它繼承自 ArgumentsHost,可以根據(jù)上下文的不同首繁, 拿到不同的對象李滴, 如果是 HTTP 請求, 則這個對象中包含 getRequest() 和 getResponse()蛮瞄,如果是 websockets 則包含 getData() 和 getClient()所坯。第二個參數(shù)是一個 Observable 流,需要讀者有一定的 Rxjs 知識挂捅。 Nest 使用 call$ 的 subscribe 結果作為最終的響應芹助。response 的 render 函數(shù)第三個參數(shù)是一個回調(diào)函數(shù),如果不傳入 express 會直接響應輸出(這樣會導致重復設置響應)闲先,如果傳入了則可以獲取到 模板引擎渲染后的 字符串状土,在這里我們需要將這個回調(diào)函數(shù) promisify 化,拿到響應然后使用 map 操作符改變流的結果伺糠,最終的響應就是模板引擎渲染后的字符串蒙谓。

最后

只在 home 模塊中使用 View 攔截器:

import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { ViewInterceptor } from 'common/interceptors/view.interceptor';
import { HomeController } from './home.controller';

@Module({
  controllers: [HomeController],
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: ViewInterceptor,
    },
  ]
})
export class HomeModule {}

同理 異常過濾器、管道 等等 也可以只作用在特定模塊上训桶,使用不同的常量就可以了累驮。
這里使用的是 APP_INTERCEPTOR 來標識提供者是一個 攔截器,如果是管道則用 APP_PIPE舵揭。

使用攔截器還可以做更多的事情谤专,例如:記錄日志、 返回緩存 等等午绳。

上一篇:7置侍、Nest.js 中的類驗證器
下一篇:9、Nest.js 中的看守器

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜡坊,隨后出現(xiàn)的幾起案子杠输,更是在濱河造成了極大的恐慌,老刑警劉巖秕衙,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抬伺,死亡現(xiàn)場離奇詭異,居然都是意外死亡灾梦,警方通過查閱死者的電腦和手機峡钓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來若河,“玉大人能岩,你說我怎么就攤上這事∠舾#” “怎么了拉鹃?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲫忍。 經(jīng)常有香客問我膏燕,道長,這世上最難降的妖魔是什么悟民? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任坝辫,我火速辦了婚禮,結果婚禮上射亏,老公的妹妹穿的比我還像新娘近忙。我一直安慰自己,他們只是感情好智润,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布及舍。 她就那樣靜靜地躺著,像睡著了一般窟绷。 火紅的嫁衣襯著肌膚如雪锯玛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天兼蜈,我揣著相機與錄音攘残,去河邊找鬼。 笑死饭尝,一個胖子當著我的面吹牛肯腕,可吹牛的內(nèi)容都是我干的献宫。 我是一名探鬼主播钥平,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涉瘾?” 一聲冷哼從身側響起知态,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎立叛,沒想到半個月后负敏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡秘蛇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年其做,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赁还。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡妖泄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艘策,到底是詐尸還是另有隱情蹈胡,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布朋蔫,位于F島的核電站罚渐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驯妄。R本人自食惡果不足惜荷并,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望青扔。 院中可真熱鬧璧坟,春花似錦、人聲如沸赎懦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽励两。三九已至黎茎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間当悔,已是汗流浹背傅瞻。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盲憎,地道東北人嗅骄。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像饼疙,于是被迫代替她去往敵國和親溺森。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容