Providers 是 Nest 的一個(gè)基本概念。許多基本的 Nest 類都可能被視為 provider - service, repository, factory, helper 等等。 他們都可以通過 constructor 注入依賴關(guān)系。 這意味著對(duì)象可以彼此創(chuàng)建各種關(guān)系,并且“連接”對(duì)象實(shí)例的功能在很大程度上可以委托給 Nest運(yùn)行時(shí)系統(tǒng)。 Provider 只是一個(gè)用 @Injectable() 裝飾器注釋的類。
在前面的章節(jié)中,我們已經(jīng)創(chuàng)建了一個(gè)簡單的控制器 CatsController ??刂破鲬?yīng)處理 HTTP 請(qǐng)求并將更復(fù)雜的任務(wù)委托給 providers。Providers 是純粹的 JavaScript 類,在其類聲明之前帶有 @Injectable()裝飾器。
由于 Nest 可以以更多的面向?qū)ο蠓绞皆O(shè)計(jì)和組織依賴性,因此我們強(qiáng)烈建議遵循 SOLID 原則。
讓我們從創(chuàng)建一個(gè)簡單的 CatsService 開始。該服務(wù)將負(fù)責(zé)數(shù)據(jù)存儲(chǔ)和檢索,其由 CatsController 使用,因此把它定義為 provider,是一個(gè)很好的選擇。因此,我們用 @Injectable() 來裝飾這個(gè)類 。
cats.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;
}
}
要使用 CLI 創(chuàng)建服務(wù)類,只需執(zhí)行 $ nest g service cats 命令。
我們的 CatsService 是具有一個(gè)屬性和兩個(gè)方法的基本類。唯一的新特點(diǎn)是它使用 @Injectable() 裝飾器。該 @Injectable() 附加有元數(shù)據(jù),因此 Nest 知道這個(gè)類是一個(gè) Nest provider。需要注意的是,上面有一個(gè) Cat 接口??雌饋硐襁@樣:
interfaces/cat.interface.ts
export interface Cat {
name: string;
age: number;
breed: string;
}
現(xiàn)在我們有一個(gè)服務(wù)類來檢索 cat ,讓我們?cè)?nbsp;CatsController 里使用它 :
cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
CatsService 是通過類構(gòu)造函數(shù)注入的。注意這里使用了私有的只讀語法。這意味著我們已經(jīng)在同一位置創(chuàng)建并初始化了 catsService 成員。
Nest 是建立在強(qiáng)大的設(shè)計(jì)模式,通常稱為依賴注入。我們建議在官方的 Angular文檔中閱讀有關(guān)此概念的精彩文章。
在 Nest 中,借助 TypeScript 功能,管理依賴項(xiàng)非常容易,因?yàn)樗鼈儍H按類型進(jìn)行解析。在下面的示例中,Nest 將 catsService 通過創(chuàng)建并返回一個(gè)實(shí)例來解析 CatsService(或者,在單例的正常情況下,如果現(xiàn)有實(shí)例已在其他地方請(qǐng)求,則返回現(xiàn)有實(shí)例)。解析此依賴關(guān)系并將其傳遞給控制器的構(gòu)造函數(shù)(或分配給指定的屬性):
constructor(private readonly catsService: CatsService) {}
Provider 通常具有與應(yīng)用程序生命周期同步的生命周期(“作用域”)。在啟動(dòng)應(yīng)用程序時(shí),必須解析每個(gè)依賴項(xiàng),因此必須實(shí)例化每個(gè)提供程序。同樣,當(dāng)應(yīng)用程序關(guān)閉時(shí),每個(gè) provider 都將被銷毀。但是,有一些方法可以改變 provider 生命周期的請(qǐng)求范圍。您可以在此處閱讀有關(guān)這些技術(shù)的更多信息。
Nest 有一個(gè)內(nèi)置的控制反轉(zhuǎn)("IoC")容器,可以解決 providers 之間的關(guān)系。 此功能是上述依賴注入功能的基礎(chǔ),但要比上面描述的要強(qiáng)大得多。@Injectable() 裝飾器只是冰山一角, 并不是定義 providers 的唯一方法。相反,您可以使用普通值、類、異步或同步工廠。此處提供了更多示例。
有時(shí),您可能需要解決一些依賴項(xiàng)。例如,您的類可能依賴于一個(gè)配置對(duì)象,但如果沒有傳遞,則應(yīng)使用默認(rèn)值。在這種情況下,關(guān)聯(lián)變?yōu)榭蛇x的, provider 不會(huì)因?yàn)槿鄙倥渲脤?dǎo)致錯(cuò)誤。
要指示 provider 是可選的,請(qǐng)?jiān)?nbsp;constructor 的參數(shù)中使用 @Optional() 裝飾器。
import { Injectable, Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
constructor(
@Optional() @Inject('HTTP_OPTIONS') private readonly httpClient: T
) {}
}
請(qǐng)注意,在上面的示例中,我們使用自定義 provider,這是我們包含 HTTP_OPTIONS自定義標(biāo)記的原因。前面的示例顯示了基于構(gòu)造函數(shù)的注入,通過構(gòu)造函數(shù)中的類指示依賴關(guān)系。在此處閱讀有關(guān)自定義提供程序及其關(guān)聯(lián)令牌的詳細(xì)信息。
我們目前使用的技術(shù)稱為基于構(gòu)造函數(shù)的注入,即通過構(gòu)造函數(shù)方法注入 providers。在某些非常特殊的情況下,基于屬性的注入可能會(huì)有用。例如,如果頂級(jí)類依賴于一個(gè)或多個(gè) providers,那么通過從構(gòu)造函數(shù)中調(diào)用子類中的 super() 來傳遞它們就會(huì)非常煩人了。因此,為了避免出現(xiàn)這種情況,可以在屬性上使用 @Inject() 裝飾器。
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
如果您的類沒有擴(kuò)展其他提供者,你應(yīng)該總是使用基于構(gòu)造函數(shù)的注入。
現(xiàn)在我們已經(jīng)定義了提供者(CatsService),并且已經(jīng)有了該服務(wù)的使用者(CatsController),我們需要在 Nest 中注冊(cè)該服務(wù),以便它可以執(zhí)行注入。 為此,我們可以編輯模塊文件(app.module.ts),然后將服務(wù)添加到@Module()裝飾器的 providers 數(shù)組中。
app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
得益于此,Nest 現(xiàn)在將能夠解決 CatsController 類的依賴關(guān)系。這就是我們目前的目錄結(jié)構(gòu):
src
├── cats
│ ├──dto
│ │ └──create-cat.dto.ts
│ ├── interfaces
│ │ └──cat.interface.ts
│ ├──cats.service.ts
│ └──cats.controller.ts
├──app.module.ts
└──main.ts
到目前為止,我們已經(jīng)討論了 Nest 如何自動(dòng)處理解決依賴關(guān)系的大多數(shù)細(xì)節(jié)。在某些情況下,您可能需要跳出內(nèi)置的依賴注入系統(tǒng),并手動(dòng)檢索或?qū)嵗峁┏绦?。我們?cè)谙旅婧喴懻搩蓚€(gè)這樣的主題。
若要獲取現(xiàn)有實(shí)例或動(dòng)態(tài)實(shí)例化提供程序,可以使用 Module reference。
要在 bootstrap() 函數(shù)內(nèi)使用提供程序(例如,對(duì)于不帶控制器的獨(dú)立應(yīng)用程序,或在引導(dǎo)過程中使用配置服務(wù)),請(qǐng)參見獨(dú)立應(yīng)用程序。
更多建議: