2020-04-15:
筆記:nestjs學習
官方介紹:
Nest 是一個用於構建高效,可擴展的 Node.js 服務器端應用程序的框架。它使用漸進式 JavaScript,內置並完全支持 TypeScript(但仍然允許開發人員使用純 JavaScript 編寫代碼)並結合了 OOP(面向對象編程),FP(函數式編程)和 FRP(函數式響應編程)的元素。
在底層,Nest使用強大的 HTTP Server 框架,如 Express(默認)和 Fastify。Nest 在這些框架之上提供了一定程度的抽象,同時也將其 API 直接暴露給開發人員。這樣可以輕松使用每個平台的無數第三方模塊。
一、安裝、創建和啟動項目:
$ npm i -g @nestjs/cli
$ nest new project-name
項目結構:入口(使用 NestFactory
用來創建 Nest 應用實例)、控制器、根模塊、服務
main.ts:
app.module.ts:
使用依賴注入的方式加入控制器和服務.
app.controller.ts:
管理路由的地方。控制器負責處理傳入的 請求 和向客戶端返回 響應 。
可以通過CLI單獨創建控制器: $ nest g controller cats
app.service.ts:
服務類,放置各種服務方法。
啟動項目:(提供了以下幾種方法)
二、控制器:
為了創建一個基本的控制器,我們必須使用裝飾器
。裝飾器將類與所需的元數據關聯,並使 Nest
能夠創建路由映射(將請求綁定到相應的控制器)。
1、路由 :
@Controller('cats') export class CatsController { @Get('food') findAll(): string { return 'This action returns all cats food'; } }
@Controller('cats'):控制器裝飾器提供路徑前綴
@Get('food') : 請求方法裝飾器聲明請求方法,並且聲稱路由映射 GET /cats/food 。Nest
以相同的方式提供其余的端點裝飾器- @Put()
、 @Delete()
、 @Patch()
、 @Options()
、 @Head()
和 @All()
。這些表示各自的 HTTP
請求方法。
2、Request:
把客戶端的請求細節(請求對象)通過裝飾器@Req 注入處理程序:
@Res()
只是 @Response()
的別名。
注意⚠️:Nest
檢測處理程序是否正在使用 @Res()
或 @Next()
,如果兩個方法都用了的話, 那么在這里的標准方式就是自動禁用此路由, 你將不會得到你想要的結果。
import { Request } from 'express';
@Controller('cats') export class CatsController { @Get() findAll(@Req() request: Request): string { return 'This action returns all cats'; } }
3、路由通配符:
路由支持re:
@Get('ab*cd') findAll() { return 'This route uses a wildcard'; } // 能匹配到abcd, ab_cd, abecd
4、狀態碼:
默認成功為200,除了POST請求返回(201),這個行為可以通過@HttpCode()更改:
@Post() @HttpCode(204) create() { return 'This action adds a new cat'; }
5、Header:
自定義響應頭:
impost {Header} from '@nestjs/common'; @Post() @Header('Cache-Control', 'none') create() { return 'This action adds a new cat'; }
6、重定向:
@Redirect()
帶有必需的 url
參數和可選的 statusCode
參數。 如果省略,則 statusCode
默認為 302
。這個重定向到內容可以動態定義:
@Get('docs') @Redirect('https://docs.nestjs.com', 302) getDocs(@Query('version') version) { if (version && version === '5') { return { url: 'https://docs.nestjs.com/v5/' }; } }
7、路由參數:
@Get(':id') findOne(@Param() params): string { console.log(params.id); return `This action returns a #${params.id} cat`; }
也可以指定參數:
@Get(':id') findOne(@Param('id') id): string { return `This action returns a #${id} cat`; }
8、范圍:
Node.js
不遵循請求/響應多線程無狀態模型,每個請求都由主線程處理。因此,使用單例實例對我們的應用程序來說是完全安全的。
9、請求負載:
定義POST請求的數據結構需要用類定義DTO(數據傳輸對象模式)。由於 TypeScript
接口在轉換過程中被刪除,所以 Nest
不能在運行時引用它們。這一點很重要,因為諸如管道之類的特性在運行時能夠訪問變量的元類型時提供更多的可能性。
// dot.ts: export class CreateCatDto { readonly name: string; readonly age: number; readonly breed: string; } // controller.ts: @Post() async create(@Body() createCatDto: CreateCatDto) { return 'This action adds a new cat'; }
三、Provider:
發揮DI的地方:通過 constructor
注入依賴關系, 這意味着對象可以彼此創建各種關系,並且“連接”對象實例的功能在很大程度上可以委托給 Nest
運行時系統。
控制器應處理 HTTP
請求並將更復雜的任務委托給 providers。Providers 是純粹的 JavaScript
類,在其類聲明之前帶有 @Injectable()
裝飾器。
1、提供服務:
需要定義接口
// cat.interface export interface Cat { name: string; age: number; breed: string; } // cat.service.ts import { Injectable } from '@nestjs/common'; import { Cat } from './interfaces/cat.interface'; @Injectable() export class CatsService { private readonly cats: Cat[] = []; create(cat: Cat) { this.cats.push(cat); } findAll(): Cat[] { return this.cats; } }
四、Module:
模塊封裝Provider。這意味着如果Provider不是當前模塊的一部分, 也不是從另外模塊(已導入)導出的,那么它就是無法注入的。
1、子模塊在根模塊中注冊:
import { Module } from '@nestjs/common'; import { CatsModule } from './cats/cats.module'; @Module({ imports: [CatsModule], }) export class ApplicationModule {}
2、共享模塊:
在 Nest 中,默認情況下,模塊是單例,因此您可以輕松地在多個模塊之間共享同一個Provider實例。
需要把 這個Provider 放到 exports
數組中:
// 現在,每個導入 CatsModule 的模塊都可以訪問 CatsService ,並且它們將共享相同的 CatsService 實例 import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], exports: [CatsService] }) export class CatsModule {}
3、Provider注入到模塊中:
多用於配置型Provider
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule { constructor(private readonly catsService: CatsService) {} }
4、全局模塊:
一次注冊,全局使用。
import { Module, Global } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Global() @Module({ controllers: [CatsController], providers: [CatsService], exports: [CatsService], }) export class CatsModule {}
@Global
裝飾器使模塊成為全局作用域。 全局模塊應該只注冊一次,最好由根或核心模塊注冊。 在上面的例子中,CatsService
組件將無處不在,而想要使用 CatsService
的模塊則不需要在 imports
數組中導入 CatsModule
。
5、動態模塊:
實際上,動態模塊擴展了模塊元數據。當您需要動態注冊組件時,這個實質特性非常有用。
例如:動態傳入provider給database模塊
// './database/database.module' import { Module, DynamicModule } from '@nestjs/common'; import { createDatabaseProviders } from './database.providers'; import { Connection } from './connection.provider'; @Module({ providers: [Connection], // 默認值 }) export class DatabaseModule { static forRoot(entities = [], options?): DynamicModule { const providers = createDatabaseProviders(options, entities); return { module: DatabaseModule, providers: providers, exports: providers, }; } } // root module import { Module } from '@nestjs/common'; import { DatabaseModule } from './database/database.module'; import { User } from './users/entities/user.entity'; @Module({ imports: [DatabaseModule.forRoot([User])], exports: [DatabaseModule], // 為了導出動態模塊,可以省略函數調用部分 }) export class ApplicationModule {}
五、中間件:
中間件是在路由處理程序 之前 調用的函數。 中間件函數可以訪問請求和響應對象,以及應用程序請求響應周期中的 next()
中間件函數。 next()
中間件函數通常由名為 next
的變量表示。
1、實現一個中間件:
必須在函數中或具有@Injectable() 的類中實現
// logger.middleware.ts import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response } from 'express'; @Injectable() export class LoggerMiddleware implements NestMiddleware { use(req: Request, res: Response, next: Function) { console.log('Request...'); next(); } }
2、應用中間件:
必須使用模塊類的 configure()
方法來設置它們。包含中間件的模塊必須實現 NestModule
接口。
可以使用 async/await
來實現 configure()
方法的異步化(例如,可以在 configure()
方法體中等待異步操作的完成)。
例如logger中間件在根模塊上使用:
// app.module.ts import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; import { CatsModule } from './cats/cats.module'; @Module({ imports: [CatsModule], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) // 引入logger中間件 .forRoutes({ path: 'cats', method: RequestMethod.GET }); // 限制使用的路由及請求方法 } }
3、關於幫助類MiddlewareConsumer
:
它提供了幾種內置方法來管理中間件。他們都可以被簡單地鏈接起來。
1、forRoutes()
可接受一個字符串、多個字符串、對象、一個控制器類甚至多個控制器類。在大多數情況下,您可能只會傳遞一個由逗號分隔的控制器列表。限制能使用這個中間件的路由或控制器。
2、exclude()方法輕松地排除某些路由。該方法不適用於函數中間件(在函數中而不是在類中定義的中間件)。此方法不排除來自更通用路由(例如,通配符)的路徑。如果您需要這種級別的控制,您應該將路徑限制邏輯直接放入中間件
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST }
)
.forRoutes(CatsController);
4、函數式中間件:
適用於如loggerMiddleware這類沒有成員,沒有額外的方法,沒有依賴關系的中間件
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
5、全局中間件:
一次性將中間件綁定到每個注冊路由。在main.ts中加入app.use(logger);
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);