点击上方 程序员成长指北,关注公众号
回复1,加入高级Node交流群
Nestjs的哲学:完全支持Typescript并解决架构问题,在服务器端提供开箱即用的应用架构
,让开发人员和团队能够创造出高可测试、可扩展、松耦合、易维护的应用。
本文主要谈及一些和其他node框架稍微差异的特性,比如依赖注入
、控制器
、管道
、拦截器
、模块
、微服务
。
一、依赖注入
Provides
是Nest的最基本的一个概念,许多基本的Nest类可能视为provider-service,repository,helper等等,在实际开发中,比如常用的service, repository。有了依赖注入我们能够提高应用程序的灵活性和模块化程度。举个例子说明:
/* 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;
}
}
CatsService
是具有一个属性和两个方法的基本类,和其他普通稍微差异的就是使用@Injectable()
装饰器,通过该装饰器使Nest知道这个类是一个provider,现在我们使用类构造函数注入该服务:
/* 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();
}
}
从上面代码来看, 我们在Controller里使用CatsService不是通过使用New来实例化, 而是在constuctor声明即可。
从上面可看出依赖注入有两个比较大的优势:
- 依赖管理交给Nest运行时系统
- 依赖项只关注类型不关注具体实例具有高度解耦性
二、控制器
控制器负责处理传入的请求和向客户端返回相应。
Controllers_1
一般的node框架可能没有控制器这个概念或者是等价路由概念,这里控制器相当于是路由资源集合。下图是一次请求生命周期:
pasted-from-clipboard
从图上可以看出请求会先走Middleware->Guards(守卫)->Interceptors(拦截器)->Pipes(管道)后才到达Controller, 那么接下来会讲解下管道和拦截器的概念。
三、管道&拦截器(Pipes,Interceptor)
管道是具有 @Injectable()
装饰器的类。管道应实现 PipeTransform
接口
Pipe_1
管道有两个类型:
- 转换:管道将输入数据转换为所需的数据输出,
- 验证:对输入数据进行验证,比如form表单提交的数据类型
拦截器是使用 @Injectable()
装饰器注解的类。拦截器应该实现 NestInterceptor
接口。
Interceptors_1
拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:
- 在函数执行之前/之后绑定额外逻辑
- 转换从函数返回的结果
- 转换从函数抛出的异常
- 扩展基本函数行为
- 根据所选条件完全重写函数 (例如, 缓存目的)
/* logging.interceptor.ts */
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
上面例子是一个统计请求时间的一个拦截器。那么基于拦截器功能我们能够实现统计时间过长的响应、统一响应体格式、捕获异常统一异常code码等功能。
四、模块
模块是具有 @Module()
装饰器的类。@Module()
装饰器提供了元数据,Nest 用它来组织应用程序结构。
Modules_1
从图片可以看出, Module的作用就是组织代码结构,CatsController
和CatsService
属于同一个应用程序域,应该考虑将它们移动到一个功能模块下,即CatsModule
/* cats/cats.module.ts */
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
代码语言:javascript复制/* app.module.ts */
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class ApplicationModule {}
目前的项目目录结构:
代码语言:javascript复制src
├──cats
│ ├──dto
│ │ └──create-cat.dto.ts
│ ├──interfaces
│ │ └──cat.interface.ts
│ ├─cats.service.ts
│ ├─cats.controller.ts
│ └──cats.module.ts
├──app.module.ts
└──main.ts
虽然我们可以使用模块来组织代码,但是在微服务流行的情况下,模块的作用就不是很大了。
五、微服务
Microservices_1
Nest
支持几种内置的传输层实现,称为传输器,负责在不同的微服务实例之间传输消息。大多数传输器本机都支持请求 - 响应和基于事件的消息样式。默认情况下,微服务通过TCP协议监听消息。
/* main.ts */
import { NestFactory } from '@nestjs/core';
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.TCP,
},
);
app.listen(() => console.log('Microservice is listening'));
}
bootstrap();
代码语言:javascript复制/* cats.controller.ts */
import { Controller, Get, Post, Body } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
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();
}
@MessagePattern({ cmd: 'findAllCats' })
async externalFindAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
为了交换消息或将事件发布到 Nest
微服务,我们使用 ClientProxy
类, 它可以通过几种方式创建实例。此类定义了几个方法,例如send()
(用于请求-响应消息传递)和emit()
(用于事件驱动消息传递),这些方法允许您与远程微服务通信。其他应用(客户端)和远程服务通信如下:
@Module({
imports: [
ClientsModule.register([
{ name: 'CAT_SERVICE', transport: Transport.TCP },
]),
]
})
代码语言:javascript复制/* client.controller.ts */
import { Inject, Controller, Get, Post, Body } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
@Controller('client')
export class ClientController {
constructor(@Inject('CAT_SERVICE') private client: ClientProxy) {}
@get()
findAllCats(): Promise<Cat[]> {
return this.client.send<number>()
}
}
除了默认的Tcp,我们还可以使用Redis、RabbitMQ、gRPC等流行的消息中间件来实现服务通信。
结束语
通过本文可以发现, Nestjs
是一个有完整应用架构的框架,和Express、Koa等框架相比,提供了从基础控制器能力,安全(认证、鉴权),数据库集成到微服务。可以说几乎和java的Spring框架一样提供了企业级服务支撑。