Nestjs是一个基于Nodejs的服务器框架,吸收和借鉴了前端框架Angular的风格,完全支持TypeScript语言,因此,对于熟悉Angular以及TypeScript的用户来说,是服务器端编程的不二选择。在底层,Nestjs支持流行的Express(默认)框架,但也可以选择Fastify框架进行开发。
1.Nestjs安装与快速启动
1.1 nestjs安装
Nestjs项目可以通过命令行,git或者手动创建三种方法开始。一般来说,更推荐使用命令行指令进行开发。使用命令行指令时,需要先安装nestjs-cli命令行模块。按照下述指令开始一个新的项目。
#npmnpm i -g @nestjs/clinest new project-name#yarnyarn global add @nestjs/cli
使用git安装时,可通过以下命令从github上复制示例项目并在此基础上开始新项目
git clone https://github.com/nestjs/typescript-starter.git project
对于高级用户来说,可能仅需要使用一部分nestjs的核心功能,在这种情况下,通过下列指令可以在项目中安装需要的模块并手动开始一个项目。
$ npm i --save @nestjs/core @nestjs/common rxjs reflect-metadata
1.2 脚手架及项目启动
安装完nestjs的命令行工具后,可通过下列命令快速搭建项目脚手架以开始新项目
nest new project-namecd project-namenpm run start#也可以以开发模式启动项目,这样项目中任何修改和变化都可以实时编译#在脚手架项目的package.json文件中可以看到更多的启动项目及测试的脚本命令#npm run start:dev
通过git命令下载的项目,只要进入项目,并按照常规的nodejs项目安装模块并启动即可
cd projectnpm installnpm run start
运行上述命令后,打开http://localhost:3000,可以看到hello world页面。
1.3 Visual Studio Code配置Nestjs项目插件
在VS Code中进行Nestjs项目开发时,除了node环境和上述nestjs命令行工具外,可以安装以下插件来提高项目开发效率和速度。
- nestjs Files,该插件可以实现nestjs部分生成命令的右键快捷操作。例如,如果要在一个名为cats的文件夹下新建Controller,可以在cats文件夹上点击右键,选择
Generate Controller并输入Controller名称cats,就可以生成cats.controller.ts文件并在其中生成并导出CatsController类。这和Angular插件的用法非常相似,实际上相当于执行nest g controller cats或者nest g service cats命令。 - NestJs Snippets,提供了大量的nestjs语法片段可快速使用。在命令栏
Ctrl+Shift+P输入n-可以看到插件支持的大部分语法和命令。 - REST Client。用于测试API的非常高效的VS Code插件,基本可以完全取代Postman。
2. nestjs脚手架项目
除了主入口文件main.ts外,nestjs项目一般模块化组成。默认的脚手架项目包括了app.module.ts,app.controller.ts,app.service.ts几个文件。和angular项目结构类似,在nestjs项目中,controller作为控制器,一般用于处理请求并返回响应,service用来实现后台逻辑。
在controller中,使用装饰器来区分不同的路径和请求。在脚手架项目中,controller默认路径的get方法返回appService.getHello()方法,appService.getHello()方法,后者返回一个Hello World字符串。在实际项目中,这样使用同步方法来返回的情况非常少见,更多的时候都是使用异步的方法返回用户请求。以下对默认脚手架项目做一些修改,以异步的方式返回用户请求。默认脚手架项目返回如下内容:
//Controller@Get()getHello(): string {return this.appService.getHello();}//ServicegetHello(): string {return 'Hello World!';}
在app.controller.ts文件中分别做如下修改:
//Controllerimport { Observable,of } from 'rxjs';getHello():Observable<string>{return of(this.appService.getHello());}
这里使用到了异步编程中最常用的rxjs模块中Observable和of,将字符串Hello World转换为异步对象,并返回到controller中通过async标识的异步函数中再响应给用户。虽然看到的同样是Hello World字符串,但在处理更复杂任务的时候,异步编程的优势就可以更好地显现出来。
3.通过nestjs响应网络请求
3.1 网络请求装饰器
nestjs可以通过装饰器响应不同类型的网络请求。nestjs默认启用express框架,系统提供的装饰器可以满足大部分网络请求,但也支持自定义装饰器来实现更多功能。常用的装饰器与express的对应关系列表如下:
| 装饰器 | 请求 |
|---|---|
| @Request() | req |
| @Response(),@Res | res |
| @Next() | next |
| @Session() | req.session |
| @Param(key?: string) | req.params / req.params[key] |
| @Body(key?: string) | req.body / req.body[key] |
| @Query(key?: string) | req.query / req.query[key] |
| @Headers(name?: string) | req.headers / req.headers[name] |
| @HttpStatus | 自定义http状态 |
3.2 nestjs支持的一些http请求和方法:
- nestjs支持@Put() 、 @Delete()、 @Patch()、 @Options()、 @Head()和 @All()装饰器以这些表示各自的 HTTP请求方法
- 支持通过
@HttpCode()装饰器来返回指定的状态码如201、404等(如@HttpCode(201)),需要从@nestjs/common导入。 - 支持
@Header装饰器来相应特定的header请求如@Header('Cache-Control', 'none'),同样需要从@nestjs/common导入。 - 支持
@Redirect装饰器来重定向资源例如@Redirect(http://www.weizhiyong.com`,301)。 - 支持模式匹配通配符,例如星号(*)可以被匹配任何字符组合
3.3 路由参数
nestjs可以使用装饰器取得路由参数或http请求中的内容,一般来说,@Param参数用来读取路由参数,如https://www.weizhiyong.com/archives/:id格式的路由,在读取id参数时可以采用以下两种方式:
@Controller('archives')@Get(':id')getId(@Param() params):string{let result=params.id;return result;}//或者@Controller('archives')@Get(':id')getId(@Param('id')id):string{return id;}
类似地,如果要从JSON格式的Body请求中取得参数,也可以使用诸如@Body()reqbody 之类的参数进行获取。
在Controller中,也可以通过host参数添加特定的域名来访问指定值(仅适用于Express),例如.Controller({ host: 'admin.example.com' })。
4.nestjs入门示例
在脚手架项目的基础上,参考官方文档,新建一个cats模块,来实现对cat的添加和基本查找功能。
在项目根目录下新建cats目录,并在其中新建cats.service.ts,cats.controller.ts和cats.module.ts文件(可以通过前节的nestjs File完成),为了实现数据接口,还需要在cats文件夹下新建dto/create-cat.dto.ts文件和interfaces/cat.interface.ts文件。
4.1 cat.interface.ts接口文件
该文件导出一个cat的接口,用于实现不同文件之间的数据交互。在实际项目中,接口文件不仅仅用于项目中不同模块之间的数据交互,也为实现前后端的统一开发提供了条件。
export interface Cat{name:string;age:number;breed:string;}
4.2 create-cat.dto.ts数据传输对象(DTO)文件
在处理数据时,通常使用dto来针对不同操作指定数据接口,和interface不同,dto往往用类的方式进行声明。本文件的内容如下:
export class CreateCatDto{readonly name:string;readonly age:number;readonly breed:string;}
4.3 cats.service.ts 服务器文件
服务在nestjs中被称为提供者(provider),与angular类似,provider通过注入的方式注入在controller或者module文件中以创建各种关系并执行不同功能,除了service外,其他被称为provider的类型还包括repository,factory和helper,所有的provider在nestjs中都通过@Injectable()装饰器来标识。除了@Injectable()装饰器外,提供者还支持@Optional()装饰器,来表示该provider是可选的。除了诸如某个类之外,提供者也可仅针对类中的某个属性进行诸如。本示例的文件内容如下:
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);return {'status':'ok'};}findAll():Cat[]{return this.cats;}}
4.4 cats.controller.ts 控制器文件
控制器通过@Controller('cats')装饰器标识路径cats。通过@Post,@Get等装饰器来实现不同的http请求方法。这里使用了异步函数的实现方式,这也是nestjs中主要使用的方式。需要注意的是,异步函数可以通过Promise或者Observable两种不同方法来实现。当使用Promise时,需要用async前缀,需要Observable时,需要从rxjs中引入Observable和of。在本例中,通过@Body() createCatDto:CreateCatDto装饰器与数据传输对象从用户请求中读取数据,并保证数据格式符合要求。
import { Controller,Get,Post,Body } from '@nestjs/common';import {CatsService} from './cats.service';import {Cat} from './interfaces/cat.interface';import {CreateCatDto} from './dto/create-cat.dto';import {Observable,of} from 'rxjs';@Controller('cats')export class CatsController {constructor(private readonly catsService:CatsService){}@Post()create(@Body() createCatDto:CreateCatDto):Observable<any>{return of(this.catsService.create(createCatDto));}@Get()findAll():Observable<Cat[]>{return of(this.catsService.findAll());}// async findAll():Promise<Cat[]>{// return this.catsService.findAll();// }}
4.5 cats.module.ts模块文件
模块文件内容如下,在模块文件中列出了本模块下的Controller和Service,如果需要引入其他模块和内容,也需要在imports中列出。如果要在模块间共享服务或者实例,也可以通过exports数组列出,例如要在其他模块中使用CatsService,可参见下文(注释掉)的exports部分代码。如果要在模块中注入提供者(比如出于配置参数的目的),也可以在模块的constructor中实现(见下列代码中注释部分)。
在nestjs中,全局模块通过@Global()装饰器标识,动态模块可以使用forRoot来同步或异步(使用Promise)返回。
import { CatsService } from './cats.service';import { CatsController } from './cats.controller';import { Module } from '@nestjs/common';@Module({imports: [],controllers: [CatsController, ],providers: [CatsService, ],//exports:[CatsService]})export class CatsModule {//constructor(private readonly someService:SomeService){}}
最后,修改app.module.ts文件以导入并使用cats模块。
import { CatsModule } from './cats/cats.module';import { Module } from '@nestjs/common';import { AppController } from './app.controller';import { AppService } from './app.service';@Module({imports: [CatsModule, ],controllers: [AppController],providers: [AppService],})export class AppModule {}
4.6 使用REST Client客户端测试
编写一个后缀为.http的文件,就可以在nest项目运行时通过REST Client插件进行测试。在REST Client中,@用于标识变量,如以下示例中的 host 变量。 ### 用来隔开不同的命令,以保证每次执行指定的变量。可以在指令行点击指令上方的send request或者使用快捷键Ctrl+Alt+R来发送指令,并验证返回情况。
@host=http://127.0.0.1:3000###{{host}}###Get {{host}}/catscontent-type: application/json###Post {{host}}/catscontent-type: application/json{"name":"Kitten1","age":1}###Post {{host}}/catscontent-type: application/json{"name":"Kitten2","age":2}
