NestJS介绍
Nest (NestJS) 是一个用于构建高效、可扩展的
Node.js服务器端应用程序的开发框架。它利用JavaScript 的渐进增强的能力,使用并完全支持TypeScript(仍然允许开发者使用纯JavaScript进行开发),并结合了 OOP (面向对象编程)、FP (函数式编程)和 FRP (函数响应式编程)。 它提供了一组丰富的工具和模块来帮助开发人员构建高效、可扩展的服务器端应用程序。这些元素可以通过依赖注入系统自动注册,并可以在整个应用程序中共享。
NestJS优势
- 构建在现代 JavaScript 栈之上,因此使用了最新的 JavaScript 技术。
- 基于 Angular 的架构和语法,提供了强大的模块化系统和依赖注入功能。
- 基于 TypeScript,提供了强类型和静态类型检查。
- 提供了丰富的工具和模块,可用于构建各种类型的服务器端应用程序,包括 RESTful API、GraphQL API、WebSocket 服务器等。
- 提供了一组可扩展的构建块,可用于快速构建应用程序。
- 提供了与主流数据库和身份验证系统的集成。
设计思想
nestJS经常被调侃为springJS,所以这里参考java项目的阿里分层规范,其架构图如下: 
终端显示层:各个端的模板渲染并执行显示的层。当前主要是velocity渲染,JS渲染,JSP渲染,移动端展示等。 开放接口层:可直接封装Service方法暴露成RPC接口;通过Web封装成http接口;进行网关安全控制、流量控制等。 Web层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。 Service层:相对具体的业务逻辑服务层。 Manager层:通用业务处理层,它有如下特征:
- 对第三方平台封装的层,预处理返回结果及转化异常信息;
- 对Service层通用能力的下沉,如缓存方案、中间件通用处理;
- 与DAO层交互,对多个DAO的组合复用。 DAO层:数据访问层,与底层MySQL、Oracle、Hbase等进行数据交互。 外部接口或第三方平台:包括其它部门RPC开放接口,基础平台,其它公司的HTTP接口。
IOC
后端系统有很多的对象,这些对象之间的关系错综复杂,如果手动创建并组装对象比较麻烦,所以后端框架一般都提供了 IoC 机制。
IoC 机制是在 class 上标识哪些是可以被注入的,它的依赖是什么,然后从入口开始扫描这些对象和依赖,自动创建和组装对象。这种依赖注入的方式叫做 Dependency Injection,简称 DI。
Nest 里通过 @Controller 声明可以被注入的 controller ,通过 @Injectable 声明可以被注入也可以注入别的对象的 provider ,然后在 @Module 声明的模块里引入。
并且 Nest 还提供了 Module 和 Module 之间的 import ,可以引入别的模块的 provider 来注入。
@Controller() //这个class要放到IOC 容器里,然后让工具自动帮我们组装好
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Inject(DepartmentService)
private departmentService: DepartmentService;
@Inject(UserService)
private userService: UserService;
}项目中的依赖注入
nest cli 会自动创建项目结构并安装依赖
npx nest new nest-ioc
npm run start构造器注入
它有一个 AppService 声明了 @Injectable,代表这个 class 可注入,那么 nest 就会把它的对象放到 IOC 容器里。
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}AppController 声明了 @Controller,代表这个 class 可以被注入,nest 也会把它放到 IoC 容器里。
AppController 的构造器参数依赖了 AppService。
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}属性注入
@Inject(DepartmentService)
private departmentService: DepartmentService;5种HTTP数据传输方式
url param
我们可以把参数写在 url 中,比如:http://guang.zxg/person/1111,这里的 1111 就是路径中的参数(url param),服务端框架或者单页应用的路由都支持从 url 中取出参数。
NestJS实现 url param 是 url 中的参数,Nest 里通过 :参数名 的方式来声明(比如下面的 :id),然后通过 @Param(参数名) 的装饰器取出来注入到 controller:
@Controller('api/person')
export class PersonController {
@Get(':id')
urlParam(@Param('id') id: string) {
return `received: id=${id}`;
}
}@Controller('api/person') 的路由和 @Get(':id') 的路由会拼到一起,也就是只有 /api/person/xxx 的 get 请求才会走到这个方法。
query
常规传递
http://guang.zxg/person?name=guang&age=20非英文的字符和一些特殊字符要经过编码,可以使用 encodeURIComponent 的 api 来编码:
const query = "?name=" + encodeURIComponent('光') + "&age=20"// ?name=%E5%85%89&age=20使用封装了一层的 query-string 库来处理
const queryString = require('query-string');
queryString.stringify({
name: '光',
age: 20
}); // ?name=%E5%85%89&age=20如果使用axios,axios 会做 url encode
NestJS实现 在 Nest 里,通过 @Query 装饰器来取:
@Controller('api/person')
export class PersonController {
@Get('find')
query(@Query('name') name: string, @Query('age') age: number) {
return `received: name=${name},age=${age}`;
}
}注意,这个 find 的路由要放到 :id 的路由前面,因为 Nest 是从上往下匹配的,如果放在后面,那就匹配到 :id 的路由了。
form-urlencoded
直接用 form 表单提交数据就是这种,它和 query 字符串的方式的区别只是放在了 body 里,前端代码使用 post 方式请求,指定 content type 为 application/x-www-form-urlencoded,用 qs 做下 url encode:
NestJS实现 用 Nest 接收的话,使用 @Body 装饰器,Nest 会解析请求体,然后注入到 dto 中。 dto 是 data transfer object,就是用于封装传输的数据的对象:
export class CreatePersonDto {
name: string;
age: number;
}import { CreatePersonDto } from './dto/create-person.dto';
@Controller('api/person')
export class PersonController {
@Post()
body(@Body() createPersonDto: CreatePersonDto) {
return `received: ${JSON.stringify(createPersonDto)}`
}
}form-data
form data 不再是通过 & 分隔数据,而是用 --------- + 一串数字做为 boundary 分隔符。因为不是 url 的方式了,自然也不用再做 url encode。 form-data 需要指定 content type 为 multipart/form-data,然后指定 boundary 也就是分割线。 很明显,这种方式适合传输文件,而且可以传输多个文件。 前端代码使用 axios 发送 post 请求,指定 content type 为 multipart/form-data
NestJS实现 Nest 解析 form data 使用 FilesInterceptor 的拦截器,用 @UseInterceptors 装饰器启用,然后通过 @UploadedFiles 来取。非文件的内容,同样是通过 @Body 来取。
import { AnyFilesInterceptor } from '@nestjs/platform-express';
import { CreatePersonDto } from './dto/create-person.dto';
@Controller('api/person')
export class PersonController {
@Post('file')
@UseInterceptors(AnyFilesInterceptor({
dest: 'uploads/'
}))
body2(@Body() createPersonDto: CreatePersonDto, @UploadedFiles() files: Array<Express.Multer.File>) {
console.log(files);
return `received: ${JSON.stringify(createPersonDto)}`
}
}json
form-urlencoded 需要对内容做 url encode,而 form-data 则需要加很长的 boundary,两种方式都有一些缺点。如果只是传输 json 数据的话,不需要用这两种。
前端代码使用 axios 发送 post 请求,默认传输 json 就会指定 content type 为 application/json,不需要手动指定
NestJS实现 同样使用 @Body 来接收,不需要做啥变动。form urlencoded 和 json 都是从 body 取值,Nest 内部会根据 content type 做区分,使用不同的解析方式。
@Controller('api/person')
export class PersonController {
@Post()
body(@Body() createPersonDto: CreatePersonDto) {
return `received: ${JSON.stringify(createPersonDto)}`
}
}