Nest.js 用了 Express 但也沒完全用,那怎么辦?
Node.js 提供了 http 模塊用于監(jiān)聽端口、處理 http 請(qǐng)求,返回響應(yīng),這也是它主要做的事情。
但是 http 模塊的 api 太過原始,直接基于它來處理請(qǐng)求響應(yīng)比較麻煩,所以我們會(huì)用 express 等庫封裝一層。
這一層做的事情就是給 request 和 response 添加了很多處理請(qǐng)求響應(yīng)的方法,滿足各種場(chǎng)景的需求,并且對(duì)路由做了處理,而且,也提供了中間件的調(diào)用鏈便于復(fù)用一些代碼,這種中間件的調(diào)用鏈叫做洋蔥模型。
但這一層沒有解決架構(gòu)問題:當(dāng)模塊多了怎么辦,怎么管理?如何劃分 Model、View、Controller?等等。
所以,用 Node.js 做后端服務(wù)時(shí)我們會(huì)再包一層,解決架構(gòu)問題,這一層的框架有 eggjs(螞蟻的)、midwayjs(淘寶的)、nestjs(國外的)。
nestjs 是其中最優(yōu)秀的一個(gè):
這一層的底層還是 express、koa 等,它只是在那些 http 框架的基礎(chǔ)上額外解決了架構(gòu)問題。
而且 nestjs 還有一點(diǎn)做的特別好,它不依賴任何一個(gè) http 平臺(tái),可以靈活的切換。
那么 nestjs 是怎么做到底層平臺(tái)的切換的呢?
想想 react 是怎么做到把 vdom 渲染到 canvas、dom、native 的?
定義一層統(tǒng)一的接口,各種平臺(tái)的 render 邏輯實(shí)現(xiàn)這些接口。這種模式叫做適配器模式。
適配器模式是當(dāng)用到第三方實(shí)現(xiàn)的某個(gè)功能時(shí),不直接依賴,而是定義一層接口,讓第三方去適配這層接口。這樣任何一個(gè)適配了這層接口的方案都能集成,也能夠靈活的切換方案。
Nest.js 對(duì)底層的 http 平臺(tái)就是提供了一層接口(HttpServer),定義了一堆用到的方法:
因?yàn)?ts 的 interface 必須實(shí)現(xiàn)所有的方法才行,為了簡(jiǎn)化,又繼承了一層抽象類 AbstractHttpAdapter,把需要實(shí)現(xiàn)的方法定義成 abstract 的。
然后 express 或者別的平臺(tái)比如 fastify 只要繼承這個(gè)適配器的類,實(shí)現(xiàn)其中的抽象方法,就能接入到 Nest.js 里:
比如 ExpressAdapter:
或者 FastifyAdapter:
這些邏輯分別放在 platform-express 和 platform-fastify 包里:
Nest.js 第一行代碼是調(diào)用 create:
create 里就會(huì)選擇一種 httpAdapter 來創(chuàng)建服務(wù):
默認(rèn)是 express:
這樣,之后調(diào)用的 request 和 response 的方法最終就都是 express 的了。
比如在 controller 里可以用 @Request 裝飾器來注入 reqeust 對(duì)象,就可以調(diào)用 reqeust 的各種方法。
import { Controller, Get, Request } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(@@Request() request: Request): string {
return 'This action returns all cats';
}
}
如果你想調(diào)用一些接口之外的特定平臺(tái)的方法的話,Nest.js 也支持,那就換用 @Req 來注入:
import { Controller, Get, Req } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(@@Req() request: Request): string {
return 'This action returns all cats';
}
}
這樣注入的就是特定平臺(tái)比如 express 的原生 request 對(duì)象,就可以直接用它的所有方法。
此外,如果真的要用 Express 平臺(tái)的特定 api 的話,在 NestFactory.create 的時(shí)候可以指定對(duì)應(yīng)的類型參數(shù),這樣就能做相應(yīng)的類型提示和檢查了:
但是這樣就和特定平臺(tái)耦合了,除非是確定不會(huì)切換平臺(tái),否則不建議這么做。
http 平臺(tái)是這么做的,同理,websocket 平臺(tái)也是這樣的:
定義了一層統(tǒng)一的接口,通過適配器的方式分別接入 socketio 和 websocket,可以靈活的切換:
圖解下 Nest.js 關(guān)于 http 、websocket 平臺(tái)的處理:
總結(jié)
Node.js 提供了 http 模塊用來監(jiān)聽端口、處理請(qǐng)求響應(yīng),但是它的 api 過于原始,所以我們會(huì)包一層,在 express 這一層提供更多好用的 request、response 的 api,但這層沒解決架構(gòu)問題,要引入 MVC、IOC 等架構(gòu),需要再包一層,用 Egg.js、Midway.js、Nest.js 這種更上層的后端框架,其中 Nest.js 是最優(yōu)秀的。
Nest.js 在和底層 http 平臺(tái)的整合上做了特殊的設(shè)計(jì),利用適配器模式,提供一層接口,讓底層平臺(tái)去適配,這樣就可以靈活的切換不同的 http 平臺(tái)了。
但它也同樣支持用特定平臺(tái)的 api,比如 controller 里可以用 @Req 注入底層的 request 對(duì)象,創(chuàng)建容器的時(shí)候也可以傳入對(duì)應(yīng)平臺(tái)的類型參數(shù)。
Nest.js 默認(rèn)使用的是 Express,但說用了 Express 也不完全對(duì),因?yàn)榭梢造`活的切換別的。這就是適配器模式的魅力。