Nest.js框架核心概念
# nest.js 核心概念
nest.js官方文档 (opens new window)
# 控制器
# 定义控制器
使用@Controller('path')来定义控制器 ,path指访问的url路径。
- @Get 获取数据
- @Post 新增数据
- @Patch 更新部分数据
- @Put 更新全部数据
- @Delete 删除数据
- @Options 对cors的跨域预检(一般用不到)
- @Head 自定义请求头
// 示例代码
@Controller('posts')
export class PostController {
@Get()
async index() {
return posts;
}
@Get(':id')
async show(@Param('id') id: number) {
}
@Post()
async store() {
}
@Patch(':id')
async update(@Param('id') id: number) {
}
@Delete(':id')
async delete(@Param('id') id: number) {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# DTO数据验证
dto是用于对请求数据结构进行定义的一个类,用于aop编程。常用于对body,query等请求数据进行验证. body和query数据的验证一般使用dto+ValidationPipe这个预定义管道. param数据的验证一般直接使用预定义或者自定义的非全局管道.
@Injectable()
export class CreatePostDto {
@MaxLength(255, {
always: true,
message: '帖子标题长度最大为$constraint1',
})
@IsNotEmpty({ groups: ['create'], message: '帖子标题必须填写' })
@IsOptional({ groups: ['update'] })
title!: string;
@IsNotEmpty({ groups: ['create'], message: '帖子内容必须填写' })
@IsOptional({ groups: ['update'] })
body!: string;
@MaxLength(500, {
always: true,
message: '帖子描述长度最大为$constraint1',
})
@IsOptional({ always: true })
summary?: string;
}
@Controller('posts')
export class PostController {
@Post()
async store(
@Body(
new ValidationPipe({
transform: true,
forbidUnknownValues: true,
validationError: { target: false },
groups: ['create'],
}),
)
data: CreatePostDto,
) {
// do something...
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 服务提供者
服务提供者就是通过类型提示或者标识符的方式使某个类或函数以依赖注入的方式在其它需要使用到它的地方进行实例化,同时也是Nestjs,Laravel,Symfony以及Spring,Angular等现代web框架的核心所在
# 基本用法
// 创建服务提供者需在class顶部添加@Injectale()装饰器 post.service.ts
@Injectable()
export class PostService {
async findAll() {
return posts;
}
async findOne(id: number) {
const post = posts.find((item) => item.id === id);
if (isNil(post))
throw new NotFoundException(`the post with id ${id} not exits!`);
return post;
}
}
// post.controller.ts
@Controller('posts')
export class PostController {
// 在构造器里注入服务提供者直接使用
constructor(private postService: PostService) {}
@Get()
async index() {
return this.postService.findAll();
}
@Get(':id')
async show(@Param('id', new ParseIntPipe()) id: number) {
return this.postService.findOne(id);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 注册与导出服务提供者
@Module({
// ...
providers: [PostService], // 服务提供者需要在providers中注册才可以被本模块的其它类注入,
exports: [PostService], // 需要在exports中导出后才能被其它模块调用
})
export class ForumModule {}
1
2
3
4
5
6
2
3
4
5
6
# 自定义服务提供者
- 值提供者: 使用useValue来构建一个提供者
- 字符串提供者: 使用字符串,比如CONNECT来构建一个提供者
- 类映射提供者: 使用一个类映射另一个类来构建一个提供者
- 构造器提供者: 使用factory来构建一个提供者
- 别名提供者: 使用useExisting来为一个提供者指定一个别名
- 异步提供者: 使用async关键字+factory构建异步提供者
// src/modules/forum/providers/example.provider.ts
@Injectable()
export class ExampleProvider {
useValue() {
return '';
}
useId() {
return '字符串提供者';
}
useAlias() {
return '别名提供者';
}
}
// src/modules/forum/providers/another.provider.ts
@Injectable()
export class AnotherProvider {
useClass() {
return '';
}
}
// src/modules/forum/providers/one.provider.ts
@Injectable()
export class OneProvider {
useClass() {
return '';
}
useFactory() {
return '构造器提供者';
}
useAsync() {
return '异步提供者';
}
}
// src/modules/forum/providers/factory.ts
export class Factory {
constructor(private one: OneProvider) {}
getContent() {
return this.one.useFactory();
}
async getPromise() {
return new Promise((resovle) => {
setTimeout(() => {
resovle(this.one);
}, 100);
});
}
}
// src/modules/forum/forum.module.ts
const exampleTest = {
useValue: () => 'useValue提供者',
useAlias: () => '别名提供者',
};
const example = new ExampleProvider();
@Module({
controllers: [PostController, ExampleController],
providers: [
PostService,
{
provide: ExampleProvider,
useValue: exampleTest,
},
{
provide: 'ID-EXAMPLE',
useValue: example,
},
{
provide: OneProvider,
useClass: AnotherProvider,
},
{
provide: 'FACTORY-EXAMPLE',
useFactory(one: OneProvider) {
const factory = new Factory(one);
return factory;
},
inject: [OneProvider],
},
{
provide: 'ALIAS-EXAMPLE',
useExisting: ExampleProvider,
},
{
provide: 'ASYNC-EXAMPLE',
useFactory: async () => {
const factory = new Factory(new OneProvider());
return factory.getPromise();
},
},
],
exports: [PostService],
})
export class ForumModule {}
// src/modules/forum/controllers/example.controller.ts
@Controller('examples')
export class ExampleController {
constructor(
private valExp: ExampleProvider,
@Inject('ID-EXAMPLE') private idExp: ExampleProvider,
@Inject('FACTORY-EXAMPLE') private ftExp: Factory,
@Inject('ALIAS-EXAMPLE') private asExp: ExampleProvider,
@Inject('ASYNC-EXAMPLE') private acExp: OneProvider,
) {}
@Get('value')
async useValue() {
return this.valExp.useValue();
}
@Get('id')
async useId() {
return this.idExp.useId();
}
@Get('factory')
async useFactory() {
return this.ftExp.getContent();
}
@Get('alias')
async useAlias() {
return this.asExp.useAlias();
}
@Get('async')
async useAsync() {
return this.acExp.useAsync();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# 注入范围
在注入服务提供者时有3种方法: DEFAULT: 默认的单例注入,每次请求都是使用同一个实例 REQUEST: 每次请求创建一个新的提供者实例,并且该实例在这次请求内被所有调用者所共享,请求完毕自动销毁 TRANSIENT:每次被使用者注入该提供者时都会创建一个新的实例,换新的请求后就不变了
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {}
// or
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 循环依赖
如果两个提供者之间互相依赖,可以通过注入forwardRef来实现
// circular-first.provider.ts
@Injectable()
export class CircularFirstProvider {
constructor(
@Inject(forwardRef(() => CircularSecondProvider))
protected second: CircularSecondProvider,
) {}
first() {
return `循环依赖1${this.second.second()}`;
}
}
// circular-second.provider.ts
@Injectable()
export class CircularSecondProvider {
constructor(
@Inject(forwardRef(() => CircularFirstProvider))
protected first: CircularFirstProvider,
) {}
second() {
return `循环依赖2`;
}
}
// example.controller.ts
@Controller('examples')
export class ExampleController {
// ...
@Get('circular')
async useCircular() {
return this.circular.first();
}
}
// 模块间的循环依赖
@Module({
imports: [forwardRef(() => CatsModule)],
})
export class CommonModule {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 模块
模块是一个功能的集合,比如user,order等, 他们包含了各自控制器,服务提供者、数据仓库等。
# 动态模块
如果要在模块导入时传入参数,则需要定义动态模块
// src/modules/core/services/common.service.ts
@Injectable()
export class CommonService {
constructor(private options: { title: string }) {}
getGlobalValue() {
return this.options;
}
}
// src/modules/core/core.module.ts
export class CoreModule {
static forRoot(options: { title: string }): DynamicModule {
return {
module: CoreModule,
global: true,
providers: [
{
provide: CommonService,
useFactory() {
const common = new CommonService(options);
return common;
},
},
],
exports: [CommonService]
};
}
}
// src/app.module.ts
@Module({
imports: [
CoreModule.forRoot({ title: '全局模块测试' }),
ForumModule,
UserModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 模块的共享
order模块需要调用user模块的服务提供者,必须先在user中使用exports导出,然后在order模块中使用imports导入才能正常使用
// src/modules/user/services/user.service.ts
@Injectable()
export class UserService {
async getUsers() {
return ['username'];
}
}
// src/modules/user/user.module.ts
@Module({
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
// src/modules/order/order.module.ts
@Module({
imports: [UserModule],
// ...
})
export class OrderModule {}
// src/modules/order/services/order.service.ts
@Injectable()
export class OrderService {
constructor(private userService: UserService) {}
async getPostUsers() {
return this.userService.getUsers();
}
// ...
}
// src/modules/order/controllers/order.controller.ts
@Controller('orders')
export class OrderController {
constructor(private orderService: OrderService) {}
@Get('users')
async users() {
return this.OrderService.getPostUsers();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 注册为全局模块
静态模块通过@Global()装饰器启用为全局模块,动态模块使用isGlobal:true启用全局模块 全局模块启用后,只要在中心模块AppModule导入,其它模块就能使用啦
// src/modules/core/services/common.service.ts
@Injectable()
export class CommonService {
getGlobalValue() {
return '全局模块测试';
}
}
// src/modules/core/services/common.service.ts
@Global()
@Module({
providers: [CommonService],
exports: [CommonService],
})
export class CoreModule {}
// src/app.module.ts
@Module({
imports: [CoreModule, ForumModule, UserModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22