认识NestJS
用于构建高效且可伸缩的服务端应用程序的渐进式 Node.js 框架。以在TypeScript和JavaScript (ES6、ES7、ES8)之上构建高效、可伸缩的企业级服务器端应用程序。它的核心思想是提供了一个层与层直接的耦合度极小、抽象化极高的一个架构体系。Nest.js目前在行业内具有很高的关注度,所以我们有必要学习一下。
Nest.js基于TypeScript 编写并且结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的相关理念。在设计上的很多灵感来自于 Angular,Angular 的很多模式又来自于 Java 中的 Spring 框架,依赖注入、面向切面编程等,所以我们也可以认为: Nest.js是Node.js 版的 Spring 框架。
Nest框架底层 HTTP平台默认是基于 Express 实现的,所以无需担心第三方库的缺失,Nest 旨在成为一个与平台无关的框架。 通过平台,可以创建可重用的逻辑部件,开发人员可以利用这些部件来跨越多种不同类型的应用程序。 从技术上讲,Nest 可以在创建适配器后使用任何Node HTTP 框架。 有两个支持开箱即用的 HTTP 平台:express 和 fastify。无论使用哪种平台,它都会暴露自己的 API。 它们分别是NestExpressApplication 和 NestFastifyApplication
特点:
完美支持 Typescript
面向 AOP 编程
支持 Typeorm
高并发,异步非阻塞 IO
Node.js 版的 spring
构建微服务应用
安装NestJS
使用 Nest CLI 建立新项目非常简单。 只要确保你已经安装了 npm,然后在你的终端中使用以下命令:
npm i -g @nestjs/cli
nest new project-name
将创建 project 目录, 安装node模块和一些其他样板文件,并将创建一个 src 目录,目录中包含几个核心文件。
src
├── app.controller.ts 带有单个路由的基本控制器示例
├── app.module.ts 应用程序的根模块
└── main.ts 应用程序入口文件。它使用 NestFactory 用来创建 Nest 应用实例。
程序入口main.ts
main.ts 包含一个异步函数,它负责引导我们的应用程序:
要创建一个 Nest 应用实例,我们使用了 NestFactory 核心类。NestFactory 暴露了一些静态方法用于创建应用实例。
create() 方法返回一个实现 INestApplication 接口的对象, 并提供一组可用的方法, 在后面的章节中将对此进行详细描述。 在上面的main.ts示例中,我们只是启动 HTTP 服务器,它允许应用程序等待入站 HTTP 请求。
请注意,使用 Nest CLI 搭建的项目会创建一个初始项目结构,我们鼓励开发人员将每个模块保存在自己的专用目录中。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
控制器
控制器负责处理传入的 请求 和向客户端返回 响应 。
控制器的目的是接收应用的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器有多个路由,不同的路由可以执行不同的操作。
为了创建一个基本的控制器,我们使用类和装饰器。装饰器将类与所需的元数据相关联,并使 Nest 能够创建路由映射(将请求绑定到相应的控制器)。
在 @Controller() 装饰器中使用路径前缀可以使我们轻松地对一组相关的路由进行分组,并最大程度地减少重复代码
要使用 CLI 创建控制器,只需执行 $ nest g controller cats 命令。
取消eslint对分号的限制"@typescript-eslint/prettier.semi":false
匹配路由
控制器是包含路由的
除了在 @Controller() 装饰器中使用路径前缀可以使我们轻松地对一组相关的路由进行分组,
还可以通过请求方式装饰器进行路由匹配,@Get()、@Post()、@Delete()、@Put()等
同时也可在请求方式装饰器加上路径进行路径匹配
@Get()
query(): string {
return '123'
}
@Post()
sava(): string {
return '123'
}
@Delete()
del(): string {
return '123'
}
@Put()
modify(): string {
return '123'
}
路由参数
参数的获取常用方式一共有五种:
params 通过添加参数@Param() param
query 通过添加参数@Query() query
application/x-www-form-urlencoded
application/json
multer/form-data
将数据放在body中,同一使用@Body() body
文件上传
文件上传需要安装 multer插件
npm i -D @types/multer
// 单文件上传
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
upload(@UploadedFile() file,@Body() body): string|Buffer {
const wirte = createWriteStream(join(__dirname, '..', 'upload', `${file.originalname}`))
wirte.write(file.buffer)
return wirte.path
}
// 多文件上传
@Post('upload')
@UseInterceptors(FilesInterceptor('files'))
UploadedFile(@UploadedFiles() files, @Body() body) {
if (!body.name || files.length === 0) {
throw new HttpException('请求参数错误.', HttpStatus.FORBIDDEN)
}
for (const file of files) {
const write = createWriteStream(join(__dirname, '..', 'upload', `${body.name}-${Date.now()}-${file.originalname}`))
write.write(file.buffer)
}
}
状态码
默认情况状态码总是200,除了POST请求(默认响应201状态码),我们可以在处理函数外添加@HttpCode(xxx)装饰器更轻松的更改此行为
通常,状态码不是固定的,而是取决于各种因素。在这种情况下,您可以使用类库特有(library-specific)的 response (通过 @Res()注 入 )对象(或者在出现错误时,抛出异常)。
// HttpCode 需要从 @nestjs/common 包导入。
@Get()
@HttpCode(200)
query(@Query() query): string {
return query;
}
@Get()
query(@Query() query, @Res() res): string {
res.status(200)
return query;
}
重定向
要将响应重定向到特定的 URL,可以使用 @Redirect() 装饰器或特定于库的响应对象(并直接调用 res.redirect())。
@Redirect() 装饰器有两个可选参数,url 和 statusCode。 如果省略,则 statusCode 默认为 302。
@Get()
@Redirect('https://nestjs.com', 301)
有时您可能想动态地决定 HTTP 状态代码或重定向 URL。通过从路由处理方法返回一个如下格式的对象:{ “url”: string, “statusCode”: number }
@Get('docs')
@Redirect('https://docs.nestjs.com', 302)
getDocs(@Query('version') version) {
if (version && version === '5') {
return { url: 'https://docs.nestjs.com/v5/' };
}
}
异步性
我们酷爱现代 Javascript,并且我们知道数据读取(data extraction)大多是异步的.这就是为什么 Nest 完美支持异步函数(Async Function)特性的原因。
每个异步函数都必须返回一个 Promise。这意味着您可以返回延迟值, 而 Nest 将自行解析它。
@Get()
async findAll(): Promise {
return [];
}
请求负载
此前我们列举的的 POST 路由处理程序样例中,处理程序没有接受任何客户端参数。我们在这里通过添加 @Body() 参数来解决这个问题。
首先(如果您使用 TypeScript),我们需要确定 DTO(数据传输对象)模式。DTO是一个对象,它定义了如何通过网络发送数据。我们可以通过使用 TypeScript 接口(Interface)或简单的类(Class)来定义 DTO 模式。有趣的是,我们在这里推荐使用类。为什么?类是 JavaScript ES6 标准的一部分,因此它们在编译后的JavaScript 中被保留为实际实体。另一方面,由于 TypeScript 接口在转换过程中被删除,所以 Nest 不能在运行时引用它们。这一点很重要,因为诸如管道(Pipe)之类的特性为在运行时访问变量的元类型提供更多的可能性。
// 创建 CreateCatDto 类
export class CreateCatDto {
readonly name: string;
readonly age: number;
readonly breed: string;
}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}