跳到主要内容

提供者

提供者是Nest中的一个基本概念许多基本的Nest类可以被视为提供者

  • 服务(services)
  • 存储库(repositories)
  • 工厂(factories)
  • 助手(helpers)

许多基本的Nest类可以被视为提供者-服务、存储库、工厂、助手等等。 提供者的主要思想是它可以作为依赖项注入。 这意味着对象之间可以创建各种关系, 并且“连接”这些对象的功能很大程度上可以委托给 Nest运行时系统

Nest.js 组件

在上一章节中,我们构建了一个简单的CatsController控制器应该处理HTTP请求并将更复杂的任务委托给提供者。 提供者在模块中声明为providers的纯JavaScript类。

提示

由于Nest能够以更加面向对象的方式设计和组织依赖关系,因此我们强烈建议遵循SOLID原则

服务

让我们创建一个简单的CatsService开始。 这个服务将负责数据存储和检索,并且设计为由CatsController使用, 因此它是定义为提供者的良好候选者。

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创建服务,只虚执行nest g service cats命令

我们的CatsService事一个基本类,具有一个属性和两个方法。 唯一的新功能是它使用@Injectable装饰器。 @Injectable()装饰器附加元数据它声明CatsService是一个可以由Nest IoC容器管理的类。 顺便说一句,这个例子也使用了Cat接口,它可能看起来像这样。

interface/cat.interface.ts
export interface Cat {
name: string;
age: number;
breed: string;
}

现在我们有了一个用于检索猫的服务类,让我们在CatsController中使用它:

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通过类构造函数注入。 请注意私有语法的使用。这种简写允许我们在同一位置立即声明和初始化catsService成员。

依赖注入

Nest是围绕通常称为依赖注入的强大设计模式构建的。 我们建议阅读Angular 官方文档中有关此概念的精彩文章。

在Nest中,借助TypeScript功能,管理依赖项变得非常容易,因为它们仅通过类型来解析。 在下面的示例中,Nest将通过创建并返回catsService(或者,在单例的正常情况下,如果已在 其他地方请求了现有实例,则返回现有实例)。 此依赖性已解决并传递给控制器的构造函数(或分配给指定的属性)。

constructor(private catsService: CatsService) {}

作用域

提供者通常具有与应用程序生命周期同步的生命周期("scope")。 当应用程序启动时,必须解决每个依赖项,因此必须实例化每个提供者程序。 同样,当应用程序关闭时,每个提供者程序都将被销毁。 但是,也有一些方法可以使您的提供者程序生命周期限定在请求范围内。

自定义提供者

Nest有一个内置的控制反转(IoC)容器可以解析提供者之间的关系此功能是上述依赖注入功能的基础, 但实际上比我们迄今为止所描述的功能要强大得多。 有多种方法可以定义提供者程序:您可以使用普通值、类以及异步或同步工厂。

可选提供者

有时,您可能存在不一定需要解决的依赖关系。 例如,您的类可能依赖于配置对象,但如果没有传递任何内容,则应使用默认值。 在这种情况下,依赖关系变得可选,因为缺少配置提供者程序不会导致错误。

要指明提供者程序时可选的,请在构造函数的签名中使用@Optional()装饰器。

import { Injectable, Optional, Inject } from '@nestjs/common';

@Injectable()
export class HtpService<T> {
constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}

请注意,在上面的示例中,我们使用的事自定义提供者程序, 这就是我们包含HTTP_OPTIONS自定义符号的原因。 前面的示例显示了基于构造函数的注入,指示通过构造函数中的类的依赖关系。

基于属性的注入

到目前为止,我们使用的技术称为基于构造函数的注入因为提供者程序是通过构造函数方法注入的。 在某些非常特殊的情况下,基于属性的注入可能很有用。例如,如果您的顶级类依赖于一个或多个提供者, 则通过从构造函数调用子类中的super()将它们一路向上传递可能会非常乏味。 为了避免这种情况,您可以再属性级别使用@Inject()装饰器。

import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
注意

如果您的类没有扩展另一个类,那么您应该始终更喜欢使用基于构造函数的注入。

提供者注册

现在我们已经定义了一个提供者(CatsService),并且有了该服务的使用者(CatsController), 我们需要向Nest注册该服务,以便它可以执行注入我们通过编辑模块文件(app.module.ts) 并将服务添加到@Module() 装饰器提供者数组中来实现此目的。

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现在能够解析CatsController类的依赖关系。 这就是我们的目录结构现在的样子:

src
|--cats
|--dto
|--create-cat.dto.ts
|--interfaces
|--cat.interface.ts
|--cats.controller.ts
|--cats.service.ts
|--app.module.ts
|--main.ts

手动实例化

到目前为止,我们已经讨论了Nest如何自动处理解决依赖关系的大部分细节。 在某些情况下,您可能需要跳出内置依赖注入系统并手动检索或实例化提供者程序。

要获取现有实例或动态实例化提供者程序,您可以使用模块引用。

bootstrap()函数中获取提供者程序(例如,对于没有控制器的独立应用程序, 或在引导期间使用配置服务)。