Nestjs中文文档(三): 提供者Providers

provider是Nest的基本概念。许多基本的Nest类都可以被视为 providerservices, repositories, factories, helpers,等。提供程序的主要思想是它可以注入依赖项。这意味着对象可以彼此创建各种关系,并且“连接”对象实例的功能在很大程度上可以委托给Nest运行时系统。Providers只是带有@Injectable()装饰器的类

image

在上一章中,我们构建了一个简单的CatsController。控制器应处理HTTP请求,并将更复杂的任务委托给Providers。Providers只是普通的JavaScript类,在@Injectable()其类声明之前带有装饰器

由于Nest允许以更面向对象的方式设计和组织依赖项,我们强烈建议遵循SOLID的原则。

提示: 设计模式中的SOLID原则,分别是单一原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。前辈们总结出来的,遵循五大原则可以使程序解决紧耦合,更加健壮。

服务 service

让我们从创建一个简单的CatsService开始.

这项服务将负责数据存储和检索( 简而言之:服务负责数据的存入和查询 ),并且是为catscocontroller设计的,因此它很适合被定义为提供者Providers

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;
  }
}
提示: 创建一个服务只需要 nest g service cats

我们的CatsService是一个具有一个属性和两个方法的基本类。唯一的新特性是它使用@Injectable()装饰器。@Injectable()附加元数据,它告诉Nest这个类是一个Nest Providers。顺便说一下,这个例子还使用了一个Cat接口,它可能看起来像这样:

export interface Cat {
  name: string;
  age: number;
  breed: string;
}

现在我们有了一个服务类来检索cats,让我们在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 readonly 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可是直接借鉴了Angular。

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

constructor(private readonly catsService: CatsService) {}

作用域

提供程序通常具有与应用程序生命周期同步的生命周期(“作用域”)。 引导应用程序时,必须解决每个依赖关系,因此必须实例化每个提供程序。 同样,当应用程序关闭时,每个提供程序都将被销毁。 但是,也有一些方法可以使提供程序的生命周期达到请求范围。 您可以在此处阅读有关这些技术的更多信息。

自定义Providers

Nest内置了IOC容器( IOC: 控制反转 )可解决提供程序之间的关系。IOC底层的依赖注入功能,但其实远比我们到目前为止所描述更加强大。

@Injectable()装饰器只是冰山一角,并不是定义Providers的唯一方法。 实际上,您可以使用纯文本值,类以及异步或同步工厂。 这里提供更多示例

可选Providers

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

要指示Providers是可选的,请在构造函数的签名中使用@Optional()装饰器。

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

基于属性的注入

到目前为止,我们已使用的技术称为基于构造函数的注入,因为Providers是通过构造方法注入的。

在某些非常特定的情况下,基于属性的注入可能会有用。 例如,如果您的顶级类依赖于一个或多个Providers,那么通过从构造函数中调用子类中的super()来将它们一直传递下去可能会非常繁琐。 为了避免这种情况,可以在属性级别使用@Inject()装饰器。

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

@Injectable()
export class HttpService<T> {
  @Inject('HTTP_OPTIONS')
  private readonly httpClient: T;
}
如果您的类没有扩展其他的Providers,则应始终使用基于构造函数的注入。

Providers的注册

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

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该类的依赖关系。

这是我们的目录结构现在的外观:( 使用类JSON的方式表明,希望能看懂 )

{
    src: {
        cats: {
          dto: {
            create-cat.dto.ts
          },
          interfaces: {
            cat.interface.ts
          },
          cats.service.ts,
          cats.controller.ts
        },
        app.module.ts,
        main.ts
    }
}

陈东民
2.1k 声望269 粉丝

坚持自我 纯粹的技术