五分鐘帶你入門基于Nodejs的強大的Web框架— NestJS
點擊上方?前端Q,關(guān)注公眾號
回復(fù)加群,加入前端Q技術(shù)交流群
簡介
Nest 是一個用于構(gòu)建高效,可擴展的
Node.js服務(wù)器端應(yīng)用程序的框架。在底層,Nest 使用強大的 HTTP Server 框架,如 Express(默認)和 Fastify。Nest 在這些框架之上提供了一定程度的抽象,同時也將其 API 直接暴露給開發(fā)人員。這樣可以輕松使用每個平臺的無數(shù)第三方模塊。
支持 TypeScript(也支持純 js 編寫代碼),默認支持最新的 ES6 等語法和特性(用 babel 做代碼轉(zhuǎn)換)。node 版本要求 >= 10.13.0, v13 版本除外。
要了解 Nest ,建議先了解一下裝飾器,因為 Nest 里面的方法很多都是以裝飾器的方式提供的,下面我簡單介紹一下。已經(jīng)了解的朋友可以跳過~
裝飾器
裝飾器(Decorator)是一種與類(class)相關(guān)的語法,用來注釋或修改類和類方法。它是一種函數(shù),寫成@ + 函數(shù)名的形式。它可以放在類和類方法的定義前面。
??@testable
??class?MyTestableClass?{
????//?...
??}
??function?testable(target)?{
????target.isTestable?=?true;
??}
??MyTestableClass.isTestable?//?true
基本上,裝飾器的行為就是下面這樣。
??@decorator
??class?A?{}
??//?等同于
??class?A?{}
??A?=?decorator(A)?||?A;
也就是說,裝飾器是一個對類進行處理的函數(shù)。裝飾器函數(shù)的第一個參數(shù),就是所要裝飾的目標(biāo)類。
注意點
裝飾器對類的行為的改變,是代碼編譯時發(fā)生的,而不是在運行時。這意味著,裝飾器能在編譯階段運行代碼。也就是說,裝飾器本質(zhì)就是編譯時執(zhí)行的函數(shù)。 裝飾器只能用于類和類的方法,不能用于函數(shù),因為存在函數(shù)提升。如果一定要裝飾函數(shù),可以采用高階函數(shù)的形式直接執(zhí)行。
Nest 基本介紹
安裝使用這里就不說了,可以到官網(wǎng)按照其引導(dǎo)來進行:https://docs.nestjs.com/first-steps。
生成的核心文件結(jié)構(gòu)為:
src
??|-app.controller.spec.ts
??|-app.controller.ts
??|-app.module.ts
??|-app.service.ts
??|-main.ts
其代表的含義分別為:
| 文件 | 含義 |
|---|---|
| app.controller.spec.ts | 控制器的單元測試 |
| app.controller.ts | 控制器邏輯文件,通常含多個路由 |
| app.module.ts | 應(yīng)用程序的根模塊 |
| app.service.ts | 服務(wù)文件 |
| main.ts | 應(yīng)用程序的入口文件,它是基于NestFactory創(chuàng)建的一個Nest應(yīng)用程序?qū)嵗?/td> |
Controller
什么是 Controller?語義化翻譯就是 控制器,它負責(zé)處理傳入的請求并將響應(yīng)結(jié)果返回給客戶端。
在 Nest 中,控制器和路由機制是結(jié)合在一起的,控制器的目的是接收應(yīng)用程序的特定請求。其路由機制控制哪個控制器接收哪些請求。通常,每個控制器都有多個路由,不同的路由可以執(zhí)行不同的操作。
我們通過裝飾器 @Controller() 來將一個類定義為控制器,如:
import?{?Controller?}?from?'@nestjs/common';
@Controller('test')
export?class?TestController?{?}
Nest 把各個HTTP的請求方法都封裝成了裝飾器,如@Get()、@Post()、@Put()、@Patch()、@Delete()、@Options()等,因此我們在實際開發(fā)中,可以直接用來裝飾對應(yīng)的請求,比如以下幾種路由:
import?{?Controller,?Get,?Post,?Body,?Put,?Param,?Delete?}?from?'@nestjs/common';
@Controller('test')
export?class?TestController?{
??@Post()
??async?create(@Body()?createTestDto:?CreateTestDto)?{
????return?'This?action?adds?a?new?test';
??}
??@Delete(':id')
??async?remove(@Param('id')?id)?{
????return?`This?action?removes?a?#${id}?test`;
??}
??@Put(':id')
??async?update(@Param('id')?id,?@Body()?updateTestDto:?UpdateTestDto)?{
????return?`This?action?updates?a?#${id}?test`;
??}
??@Get(':id')
??async?findOne(@Param('id')?id)?{
????return?`This?action?returns?a?#${id}?test`;
??}
}
Provider
什么是 Provider?語義化翻譯就是 提供者,在 Nest 中,除了控制器以外,幾乎所有的東西都可以被視為提供者,比如service、repository、factory、helper等等。他們都可以通過構(gòu)造函數(shù)注入依賴關(guān)系,也就是說,他們之間可以創(chuàng)建各種關(guān)系。而提供者只不過是一個用 @Injectable() 裝飾器的簡單類。
在類聲明上,定義 @Injectable() 裝飾器,即可將該類定義為提供者。如:
import?{?Injectable?}?from?'@nestjs/common';
@Injectable()
export?class?TestService?{
??private?readonly?test:?Test[]?=?[];
??async?create(createTestDto:?CreateTestDto)?{
????this.test.push(createTestDto);
??}
??async?remove(id:?number)?{
????this.test.splice(test.indexOf(test.find(t?=>?t.id?===?id)),?1);
??}
??async?update(id:?number,?updateTestDto:?UpdateTestDto)?{
??????if(updateTestDto.name)?this.test.find(t?=>?t.id?===?id).name?=?updateTestDto.name;
??}
??async?findOne(id:?number):?Test?{
??????return?this.test.find(t?=>?t.id?===?id);
??}
}
Module
Module也是一個裝飾器,Nest 使用 Module來組織應(yīng)用程序結(jié)構(gòu),每個應(yīng)用程序至少有一個模塊,即根模塊。根模塊是 Nest 開始排列應(yīng)用程序樹的地方。當(dāng)應(yīng)用程序很小時,根模塊可能是應(yīng)用程序中唯一的模塊。不過,大多數(shù)情況下,都有很多模塊,每個模塊都有一組與其密切相關(guān)的功能。
模塊,是用來組織 Controller 和 Provider,為他們在 同模塊范圍內(nèi) 建立依賴關(guān)系的。比如上面的 Controller 和 Provider,我們建立關(guān)系:
import?{?Module?}?from?'@nestjs/common';
import?{?TestController?}?from?'./test.controller';
import?{?TestService?}?from?'./test.service';
@Module({
??controllers:?[TestController],
??providers:?[TestService],
})
export?class?TestModule?{}
只有這樣,Nest 才可以在 TestController 中通過其構(gòu)造函數(shù),依賴注入 TestService,才可以在 controller 中調(diào)用 service 服務(wù)。
而當(dāng)不同模塊之間的服務(wù)需要互相調(diào)用時,我們就要在對應(yīng)的模塊之間導(dǎo)出和導(dǎo)入了,例如:
import?{?Module?}?from?'@nestjs/common';
import?{?TestController?}?from?'./test.controller';
import?{?TestService?}?from?'./test.service';
@Module({
??imports:?[],
??controllers:?[TestController],
??providers:?[TestService],
??exports:?[TestService]
})
export?class?TestModule?{}
全局模塊
如果你必須在很多地方都導(dǎo)入相同的模塊,這會出現(xiàn)大量的冗余。但是 Nest 將提供者封裝在模塊范圍內(nèi),如果不導(dǎo)入模塊,就無法在其他地方使用他們導(dǎo)出的提供者。但是有時候,你可能只是想提供一組隨時可用的提供者,例如:helpers、database connection 等等。針對這種特殊情況,Nest 提供了一個很強大的功能 —— 全局模塊,全局模塊一旦被導(dǎo)入到根模塊,在其他所有模塊中即可輕松的使用這個全局模塊導(dǎo)出的提供者,而且也不用在其他模塊導(dǎo)入這個全局模塊。
將一個模塊定義為全局模塊,只需要在類上額外增加一個裝飾器 @Global() 即可,示例:
import?{?Module,?Global?}?from?'@nestjs/common';
@Global()
@Module({
??imports:?[],
??controllers:?[],
??providers:?[],
??exports:?[]
})
export?class?TestModule?{}
動態(tài)模塊
Nest 模塊系統(tǒng)有一個稱為動態(tài)模塊的特性。它能夠讓我們創(chuàng)建可定制的模塊,當(dāng)導(dǎo)入模塊并向其傳入某些選項參數(shù),這個模塊根據(jù)這些選項參數(shù)來動態(tài)的創(chuàng)建不同特性的模塊,這種通過導(dǎo)入時傳入?yún)?shù)并動態(tài)創(chuàng)建模塊的特性稱為 動態(tài)模塊。
@Module({})
export?class?AppModule?{
??static?register(CustomController):?DynamicModule?{
????return?{
??????module:?AppModule,
??????controllers:?[CustomController],
??????providers:?[],
??????exports:?[],
????};
??}
}
NestFactory
在 Nest 中,我們通過在 main 入口中調(diào)用 NestFactory.create 來創(chuàng)建 Nest 應(yīng)用實例,Nest 創(chuàng)建的實例默認是 express 實例。main 入口示例:
import?{?NestFactory?}?from?"@nestjs/core";
import?{?AppModule?}?from?"./app.module";
async?function?bootstrap()?{
??//?使用?NestFactory?創(chuàng)建一個根模塊為?AppModule?的?Nest?app
??const?app?=?await?NestFactory.create(AppModule);
??//?將這個 Nest app 監(jiān)聽本地的 3000?端口,即:http://localhost:3000
??await?app.listen(3000);
}
bootstrap();
Middleware
Middleware 即中間件,它是請求發(fā)出者和路由處理器之間的橋梁,可以透明的、輕松的訪問請求和響應(yīng)對象。在 Nest 中,中間件可以有多個,他們之間使用 next() 方法作為連接,連接后的所有中間件將在整個請求-響應(yīng)周期內(nèi)通過 next()依次執(zhí)行。
注:默認情況下,Nest 中間件等同于 Express 中間件。Nest 中間件可以是一個函數(shù),也可以是一個帶有
@Injectable()裝飾器的類,且該類應(yīng)該實現(xiàn) NestMiddleware 接口,而函數(shù)沒有任何特殊要求。如下簡單示例:
//?帶有?`@Injectable()`?裝飾器的類中間件
import?{?Injectable,?NestMiddleware?}?from?'@nestjs/common';
@Injectable()
export?class?OAAuthMiddleware?implements?NestMiddleware?{
??use(req:?Request,?res:?Response,?next:?NextFunction)?{
????console.log('Request...:?',?req);
????next();
??}
}
//?函數(shù)中間件
export?function?OAAuthMiddleware(req,?res,?next)?{
??console.log('res:?',?res);
??next();
}
與Provider和Controller一樣,中間件也能夠通過構(gòu)造函數(shù)注入屬于同一模塊的依賴項。
全局中間件使用
為了將中間件一次性綁定到每個注冊的路由,我們可以通過 Nest 實例中的 use() 方法使用:
const?app?=?await?NestFactory.create(ApplicationModule);
//?這里必須使用函數(shù)中間件
app.use(OAAuthMiddleware);
await?app.listen(3000);
模塊中使用
既然中間件是請求發(fā)出者和路由處理器之間的橋梁,那么他就應(yīng)該在一個模塊的入口,即 XXXModule 類中被使用。在 Nest 中,我們只需要在模塊類中實現(xiàn) NestModule 接口:
import?{?Module,?NestModule,?MiddlewareConsumer?}?from?'@nestjs/common';
import?{?OAAuthMiddleware?}?from?'./common/middlewares/oAAuthMiddleware.middleware';
import?{?TestModule?}?from?'./test/test.module';
@Module({
??imports:?[TestModule],
})
export?class?ApplicationModule?implements?NestModule?{
??configure(consumer:?MiddlewareConsumer)?{
????consumer
??????.apply(OAAuthMiddleware)
??????.forRoutes('test');
??}
}
在上面的例子中,我們?yōu)?/test 路由處理器 (@TestController('/test')) 設(shè)置了鑒權(quán)中間件。如果只需要給 /test 路由中的某幾個請求方法設(shè)置這個中間件,那只需要改變一下 forRoutes() 方法中的參數(shù)即可:forRoutes({ path: 'test', method: RequestMethod.GET }),此時,只有 GET 請求才會被中間件攔截。如果存在很多路由規(guī)則,也可以使用通配符來處理。如:
forRoutes({?path:?'ab*cd',?method:?RequestMethod.ALL?})
而當(dāng)你想排除一個控制器類中的某些路由不使用中間件時,使用 exclude() 方法即可,如:
import?{?Module,?NestModule,?MiddlewareConsumer?}?from?'@nestjs/common';
import?{?OAAuthMiddleware?}?from?'./common/middlewares/oAAuthMiddleware.middleware';
import?{?TestModule?}?from?'./test/test.module';
@Module({
??imports:?[TestModule],
})
export?class?ApplicationModule?implements?NestModule?{
??configure(consumer:?MiddlewareConsumer)?{
????consumer
??????.apply(OAAuthMiddleware)
??????.exclude(
????????{?path:?'test',?method:?RequestMethod.GET?},
??????)
??????.forRoutes('test');
??}
}
最后
了解了 Nest 的基本概念之后,可以安裝@nestjs/cli來體驗一下 Nest 項目,這里給大家出個思考題,如何把 Nest 項目抽離為 runtime(Nest框架) + faas(入口文件) 的形式呢?
參考資料
https://www.bookstack.cn/read/es6-3rd/docs-decorator.md https://docs.nestjs.com http://ourjs.com/wiki/view/nestjs/02.controller

往期推薦



歡迎加我微信,拉你進技術(shù)群,長期交流學(xué)習(xí)...
歡迎關(guān)注「前端Q」,認真學(xué)前端,做個專業(yè)的技術(shù)人...


