Providers
Providers 是 Nest 的一个基本概念。许多基本的 Nest 类可能被视为 provider - service, repository, factory, helper 等等。 他们都可以通过 constructor 注入依赖关系。 这意味着对象可以彼此创建各种关系,并且“连接”对象实例的功能在很大程度上可以委托给 Nest运行时系统。 Provider 只是一个用 @Injectable() 装饰器注释的类。
在 NestJS 中有五种主要的 Provider:useClass(类提供者)、useValue(值提供者)、useFactory(工厂提供者、异步工厂提供者)、useExisting(别名提供者)
1.类提供者 useClass
类提供者是 Provider 中常用的一种,它可以让我们通过依赖注入的方式来获取类的实例。这对复用和测试非常有价值,因为我们可以在测试时使用模拟的实例来替换真正的服务,
首先生成一个Service文件
nest g s modules/content/services/posts --no-spec --flat
前面第一天提过,controller:控制器文件,可以简单理解为路由文件,Service:服务文件,写业务逻辑,当然controller也可以写业务逻辑但是这样就显得混乱了点,所以把业务逻辑抽出来写在Service中
将controller中的逻辑抽离到services中改造下
import { Injectable } from '@nestjs/common';
import { CreatePostsDto } from '../dtos/create-posts.dto';
import { UpdatePostsDto } from '../dtos/update-posts.dto';
export interface PostEntity {
id: number;
title: string;
summary?: string;
body: string;
}
@Injectable()
export class PostsService {
protected posts: PostsEntity[] = [
{ id: 1, title: '标题1', body: '内容1' },
{ id: 2, title: '标题2', body: '内容2' },
{ id: 3, title: '标题3', body: '内容3' },
{ id: 4, title: '标题4', body: '内容4' },
{ id: 5, title: '标题5', body: '内容5' },
{ id: 6, title: '标题6', body: '内容6' },
];
async findAll() {
return this.posts;
}
async findOne(id: number) {
// @Param(key?: string): 获取url中的params参数,比如 this.posts/1
return this.posts.find((v) => v.id === Number(id));
}
async createPost(
data: CreatePostsDto, // 使用Dto
) {
// @Body() 获取请求体
const newPost: PostsEntity = { ...data, id: this.posts[this.posts.length - 1].id + 1 };
this.posts.push(newPost);
return this.posts;
}
async upDatePost(data: UpdatePostsDto) {
const { id } = data;
this.posts = this.posts.map((item) =>
item.id === Number(id) ? { ...item, ...data } : item,
);
return this.posts;
}
async deletePost(id: number) {
this.posts = this.posts.filter((v) => v.id !== Number(id));
return this.posts;
}
}
在modules的providers中注入最常用的方式是providers: [PostsService],这是一种语法糖的写法,它的全称是providers: [{provide: PostsService,useClass: PostsService}],当实例和名称一致的时候可以采取简写,此外也可以自定义名称譬如写作: [{provide: ‘xxx’,useClass: PostsService}],那就需要在controller注入的时候使用@Inject('xxx')注入了
import { Module } from '@nestjs/common';
import { PostsController } from './controllers/posts.controller';
import { PostsService } from './services/posts.service';
@Module({
providers: [PostsService],
controllers: [PostsController],
})
export class ContentModule {}
把controller里面的业务逻辑抽离,并且注入PostsService使用
import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';
// 引入Pipe
import { CreatePostsDto } from '../dtos/create-posts.dto'; // 引入Dto
import { UpdatePostsDto } from '../dtos/update-posts.dto';
import { PostsService } from '../services/posts.service';
@Controller('posts')
export class PostsController {
//在此注入PostsService,若modules写作[{provide: ‘xxx’,useClass: PostsService}]
//注入则应写作constructor(@Inject('xxx') private postsService: PostsService)指定注入的类
constructor(private postsService: PostsService) {}
@Get() // 查询全部
async queryAll() {
return this.postsService.findAll();
}
@Get(':id') // 查询单条 ':id'动态id
async queryOne(@Param('id') id: number) {
return this.postsService.findOne(id);
}
@Post() // 新增
async addPost(
@Body() // 使用Pipe
data: CreatePostsDto, // 使用Dto
) {
return this.postsService.createPost(data);
}
@Patch(':id') // 更新部分数据
async upDate(@Body() data: UpdatePostsDto) {
return this.postsService.upDatePost(data);
}
@Delete(':id') // 删除
async delPost(@Param('id') id: number) {
return this.postsService.deletePost(id);
}
}
2.值提供者 useValue
值提供者是最直接的一种类型,它直接返回一个常量或者预定义的值。且更常见的用例是,将应用的配置注入到需要读取配置的服务中。
...
const firstObj = {
useValue: () => 'useValue提供者',
useAlias: () => '别名提供者',
};
@Module({
providers: [
...,
{
provide: 'first',
useValue: firstObj,
},{
provide: 'second',
useValue: ['1', '2', '3'],
}],
controllers: [PostsController],
})
export class ContentModule {}
3.工厂提供者 useFactory
工厂提供者是最灵活的 Provider 形式。工厂函数可以返回任意值,并且可以用来执行复杂的同步或异步操作。甚至可以根据运行时逻辑返回不同的结果或者根据不同的运行环境提供不同的实现。
...
@Module({
providers: [
...,
{
provide: 'factory',
inject: [SecondService], // inject: 要注入Factory函数上下文的可选提供者列表。
useFactory:(second: SecondService) =>{
// useFactory:返回要注入的提供程序实例的工厂函数
return new FourthService(second);
},
}],
controllers: [PostsController],
})
export class ContentModule {}
4.异步工厂提供者 useFactory
异步工厂提供者与工厂提供者在原则上是相似的,但它们返回一个 Promise 或 Observable。当 Promise 解析或者 Observable 发射出结果时,这个结果就会在应用中作为注入的值。这项特性对于需要进行异步操作来提供值的需求场景非常有用,比如数据库连接,远程配置等。
...
@Module({
providers: [
...,
{
provide: 'async',
useFactory: async () => {
return new Promise((resovle) => {
setTimeout(() => {
resovle('async');
}, 3000);
});
},
},],
controllers: [PostsController],
})
export class ContentModule {}
5.别名提供者 useExisting
别名提供者允许我们为提供者赋予别名,这样我们可以在不同上下文中引用并使用同样的值。这在某些需要多次引用同一个提供者或者希望使用更具语义化名称的场景中很有用。
...
@Module({
providers: [
...,
{
provide: 'alias',
useExisting: AliasService,
},],
controllers: [PostsController],
})
export class ContentModule {}
拓展
ts的constructor有三种用法,其一:初始化类的属性,其二:进行属性校验和初始化,其三:依赖注入
//1.初始化类的属性,在构造函数中,可以使用参数 name 和 age 来初始化类的属性 this.name 和 this.age,并且确保每个实例都有这些属性。
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
//2.进行属性校验和初始化,校验age大于0时初始化
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
if (age < 0) {
throw new Error("年龄必须是正整数");
}
this.age = age;
}
}
//3.依赖注入
class Home {
// 一些方法
doSomeThing(){
}
}
class Person {
// 使用 this.home 调用里面的方法
constructor(private home: Home) {}
start(){
console.log(this.home.doSomeThing())
}
}