Nest快速上手

快速创建

在学习 NestJS 之前请先确保操作系统中安装过 Node.js(版本 >= 16)

本篇文章使用的Node.js版本是16.19.0。

使用 Nest 命令行接口 设置新项目非常简单。安装 npm 后,你可以在操作系统终端中使用以下命令创建一个新的 Nest 项目:

$ npm i -g @nestjs/cli
$ nest new nest-study

创建项目时,要求你选择包管理工具(npm,yarn,pnpm),本篇文章选择的是 npm 作为包管理工具。

然后使用npm run start命令运行项目,项目的默认端口是3000。可以使用 进行访问。

http://localhost:3000
还可以使用 npm run start:dev 命令运行项目,此命令将监视你的文件,自动重新编译并重新加载服务器。

基本概念

运行起来后,来分析项目目录核心结构,重点关注 src 文件夹里的内容。

文件名描述
app.controller.spec.ts具有单一路由的基本控制器
app.controller.ts控制器的单元测试
app.module.ts应用的根模块
app.service.ts具有单一方法的基本服务
main.ts使用核心函数 NestFactory 创建 Nest 应用实例的应用入口文件

首先从入口文件 main.ts 开始介绍。

// 引入 NestJS 的 NestFactory 类,用于创建 NestJS 应用程序的实例 
import { NestFactory } from '@nestjs/core';
// 引入应用的主模块,它是 NestJS 应用程序的根模块
import { AppModule } from './app.module';

// 定义一个异步的 bootstrap 函数,用于启动 NestJS 应用程序 
async function bootstrap() {
  // 使用 NestFactory 的 create 方法创建 NestJS 应用程序的实例,传入应用的主模块 AppModule
  const app = await NestFactory.create(AppModule);
  // 调用 app 实例的 listen 方法,启动 HTTP 服务器并监听 3000 端口  
  // 注意:listen 方法也是异步的,因此使用 await 等待其完成
  await app.listen(3000);
}
// 调用 bootstrap 函数启动 NestJS 应用程序 
bootstrap();

要创建 Nest 应用实例,就需要使用到核心 NestFactory 类。使用它的create() 方法返回一个应用对象,通过调用这个实例的 listen() 方法,启动了一个 HTTP 服务器并让它监听在 3000 端口上。

接下来看 ./app.module.ts 文件中的内容。

// 引入 NestJS 的 Module 装饰器,用于定义 NestJS 的模块  
import { Module } from '@nestjs/common';  
// 引入当前应用中定义的 AppController 类,它负责处理进入的 HTTP 请求  
import { AppController } from './app.controller';  
// 引入当前应用中定义的 AppService 类,它封装了应用中的业务逻辑  
import { AppService } from './app.service';  
  
// 使用 @Module 装饰器来定义一个模块,这里是根模块 AppModule  
@Module({  
  // imports 数组用于导入其他模块,以便在当前模块中使用它们的功能  
  // 在这个例子中,根模块没有导入其他模块,所以数组为空  
  imports: [],  
  // controllers 数组列出了当前模块中所有的控制器类  
  // 这里只列出了 AppController,因为它是处理 HTTP 请求的类  
  controllers: [AppController],  
  // providers 数组列出了当前模块中所有的服务提供者  
  // 这些服务提供者可以是任何值或类,但通常它们封装了应用的业务逻辑  
  // 在这个例子中,只列出了 AppService,它可能包含了一些与数据库交互、处理业务逻辑等功能的方法  
  providers: [AppService],  
})  
// 导出 AppModule 类,使其可以在其他模块中被导入  
export class AppModule {}

在这个 AppModule 类中,我们使用 @Module 装饰器来定义了一个 NestJS 模块。这个模块是应用的根模块,它包含了应用所需的控制器(AppController)和服务提供者(AppService)。imports 数组用于导入其他模块,但在这个例子中它是空的,因为根模块不需要导入其他模块。controllers 数组列出了所有控制器类,这些类负责处理进入的 HTTP 请求。providers 数组列出了所有服务提供者,这些服务提供者可以是任何值或类,但在这个例子中,它只包含了一个服务类 AppService,这个类可能封装了应用的业务逻辑。

然后来看 app.controller.ts 文件中的内容

// 引入 NestJS 的 Controller 和 Get 装饰器,用于定义路由处理器  
import { Controller, Get } from '@nestjs/common';  
// 引入 AppService 类,它可能封装了应用的业务逻辑,包括返回问候语的方法  
import { AppService } from './app.service';  
  
// 使用 @Controller 装饰器定义一个控制器类 AppController  
// 由于没有指定任何路由路径,这个控制器将处理根路径 '/' 的请求  
@Controller()  
export class AppController {  
  // 在构造函数中,通过依赖注入的方式获取 AppService 的实例  
  // private readonly appService: AppService 表示这是一个只读属性,其值将在构造函数中初始化  
  constructor(private readonly appService: AppService) {}  
  
  // 使用 @Get 装饰器定义一个路由处理器方法 getHello  
  // 这个方法将处理 GET 请求,并且由于它没有被指定任何路由路径,它将处理根路径 '/' 的 GET 请求  
  @Get()  
  getHello(): string {  
    // 调用 AppService 的 getHello 方法获取问候语,并返回这个问候语  
    return this.appService.getHello();  
  }  
}

在这个 AppController 类中,我们定义了一个路由处理器方法 getHello,它使用 @Get() 装饰器来指定这个方法将处理对根路径 / 的 GET 请求。方法内部,我们通过调用 AppServicegetHello 方法来获取一个问候语字符串,并将这个字符串返回给客户端。通过这种方式,NestJS 的控制器负责处理 HTTP 请求,并将业务逻辑委托给服务提供者(在这个例子中是 AppService)。

然后来看 app.service.ts 文件中的内容。

// 引入 NestJS 的 Injectable 装饰器,用于标记一个类为可注入的服务提供者  
import { Injectable } from '@nestjs/common';  
  
// 使用 @Injectable 装饰器标记 AppService 类为可注入的服务提供者  
@Injectable()  
export class AppService {  
  // 定义一个名为 getHello 的方法,该方法没有接收任何参数  
  // 方法返回一个字符串 'Hello World!',这个字符串可以被视为一个简单的问候语  
  getHello(): string {  
    return 'Hello World!';  
  }  
}

在这个 AppService 类中,我们使用 @Injectable() 装饰器来标记这个类是一个可注入的服务提供者。这意味着 NestJS 的依赖注入系统可以创建这个类的实例,并将其注入到需要它的地方,比如控制器中。getHello 方法是一个简单的示例,它返回了一个固定的字符串 'Hello World!',但在实际应用中,这个方法可能会包含更复杂的逻辑,比如从数据库中查询数据、调用其他服务或执行其他业务逻辑。

再来看 app.controller.spec.ts 文件里的内容。

// 引入 NestJS 测试模块的相关类和装饰器  
import { Test, TestingModule } from '@nestjs/testing';  
// 引入被测试的控制器类 AppController  
import { AppController } from './app.controller';  
// 引入被控制器依赖的服务类 AppService  
import { AppService } from './app.service';  
  
// 使用 describe 定义一个测试套件,用于测试 AppController  
describe('AppController', () => {  
  // 声明一个变量 appController,用于存储 AppController 的实例  
  let appController: AppController;  
  
  // 使用 beforeEach 钩子函数在每个测试用例执行之前运行  
  // 它将创建一个测试模块,并编译它,然后从中获取 AppController 的实例  
  beforeEach(async () => {  
    // 使用 Test.createTestingModule 方法创建一个测试模块  
    // 传入一个对象,指定控制器数组(包含 AppController)和服务提供者数组(包含 AppService)  
    const app: TestingModule = await Test.createTestingModule({  
      controllers: [AppController], // 指定测试模块中要包含的控制器  
      providers: [AppService], // 指定测试模块中要包含的服务提供者  
    }).compile(); // 编译测试模块  
  
    // 使用 app.get 方法从测试模块中获取 AppController 的实例  
    // 并将其赋值给之前声明的 appController 变量  
    appController = app.get<AppController>(AppController);  
  });  
  
  // 使用 describe 定义一个子测试套件,用于测试根路由('/')的 GET 请求  
  describe('root', () => {  
    // 使用 it 定义一个测试用例,测试 getHello 方法是否返回 "Hello World!"  
    it('should return "Hello World!"', () => {  
      // 调用 appController 实例的 getHello 方法,并使用 expect 断言其返回值是否为 'Hello World!'  
      expect(appController.getHello()).toBe('Hello World!');  
    });  
  });  
});

在这个文件中使用 Jest(NestJS 默认使用的测试框架)编写了单元测试示例,用于测试 AppController 类的 getHello 方法。它使用了 NestJS 提供的测试工具来创建一个测试模块,该模块包含了 AppController 和它依赖的 AppService。然后,它编译这个测试模块,并从中获取 AppController 的实例,最后通过调用 getHello 方法并断言其返回值来验证 AppController 的行为是否符合预期。

Nest基本命令

NestJS 提供了一套丰富的命令来帮助开发者快速搭建和管理项目,这些命令主要通过 Nest CLI(Nest 命令行接口)来执行。

以下是一些常用的 NestJS 命令:

名称别名描述
newn搭建新的标准模式应用程序,其中包含运行所需的所有样板文件
generateg基于原理图生成和/或修改文件
build将应用程序或工作区编译为输出文件夹
start编译并运行应用程序(或工作区中的默认项目)
add编译并运行应用程序(或工作区中的默认项目)
infoi显示有关已安装的嵌套包的信息和其他有用的系统信息

generate 后可以加的选项如下:

名称别名描述
applicationapplication在单存储库中生成一个新应用程序(如果是标准结构,则转换为单存储库)
classcl生成一个新类
configurationconfigGenerate a CLI configuration file
controllerco生成控制器声明
decoratord生成自定义修饰器
filterf生成过滤器声明
gatewayga生成网关声明
guardgu生成保护声明
interceptoritc生成侦听器声明
interfaceitf生成接口
middlewaremi生成中间件声明
modulemo生成模块声明
pipepi生成管道声明
providerpr生成提供程序声明
resolverr生成解析程序声明
services生成服务声明
librarylib在单存储库中生成一个新库(如果是标准结构,则转换为单存储库)
sub-appappGenerate a new application within a monorepo
resourceres生成新的 CRUD 资源
//创建user模块,也可以使用 mo 别名生成
nest g module user 
nest g mo user 

//创建user控制器,也可以使用 co 别名生成
nest g controller user
nest g co user

//创建user服务,也可以使用 s 别名生成
nest g service user
nest g s user

像上方形式执行三次命令未免有些麻烦,你可以执行下方命令也可以实现创建模块、控制器以及服务的效果

nest g resource user

不过需要注意的是,在执行时需要选择风格,本篇文章选择的是 RESTful 风格。

RESTful风格

什么是RESTful

RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用 XML 格式定义或 JSON 格式定义。

RESTful和传统风格对比

请求方式含义传统风格RESTful
get查询所有http://localhost:3000/user/http://localhost:3000/user
查询单个http://localhost:3000/user/list?id=1http://localhost:3000/user/1
post增改http://localhost:3000/user/addhttp://localhost:3000/user
delete删除http://localhost:3000/user/deletehttp://localhost:3000/user
put更新http://localhost:3000/user/updatehttp://localhost:3000/user

版本控制

如果想在Nest中使用,需要在./src/main.ts中添加以下内容:

import { NestFactory } from '@nestjs/core';
import { VersioningType } from "@nestjs/common"
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableVersioning({
	  type: VersioningType.URI
  })
  await app.listen(3000);
}
bootstrap();

以user为例,在user.controller.ts中,将@Controller('user')修改为

@Controller({
    path: "user",
    version: "1"
})

此时我们再请求http://localhost:3000/user就会显示404,需要在 user 前加上版本号,即http://localhost:3000/v1/user

像上方的做法,就将所有请求方式都设置了版本。那如果我们只想设置某一种请求方式呢?

当然也是可以的,如给 user 中的 get 请求加上版本。

  @Get(':id')
  @version('1')
  findOne(@Param('id') id: string) {
    return this.userService.findOne(+id);
  }

code码规范

code码说明
200 OK成功
304 Not Modified协商缓存
400 Bad Request参数错误
401 Unauthorizedtoken错误
403 Forbidden referer origin验证失败
404 Not Found接口不存在
500 Internal Server Error服务端错误
502 Bad Gateway上游接口有问题或者服务器问题

控制器

控制器负责处理传入请求并向客户端返回响应。简单点说,就是获取前端传来的参数。
NestJS 提供了方法参数装饰器 用来帮助我们快速获取参数 如下:

装饰器用法
@Request()req
@Response()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]

获取get请求传参

可以使用Request装饰器 或者 Query 装饰器
方法一:Request装饰器

import { Controller, Get, Post, Body, Patch, Param, Delete, Request } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller({path:'user',version:"1"})
export class UserController {
  constructor(private readonly userService: UserService) {}
  
  @Get()
  findAll(@Request() req) {
	return {
		code: 200,
		msg: req.query.name
	}
  }
}

方法二:Query 装饰器

import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller({path:'user',version:"1"})
export class UserController {
  constructor(private readonly userService: UserService) {}
  
  @Get()
  findAll(@Query() query) {
	return {
		code: 200,
		msg: query.name
	}
  }
}

两种方法使用 Postman 发请求均可得到下图结果
Get

post 获取参数

可以使用 Request 装饰器或者 Body 装饰器
方法一:Request 装饰器

import { Controller, Get, Post, Body, Patch, Param, Delete, Request } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller({path:'user',version:"1"})
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  create(@Request() req) {
    return {
		code: 200,
		msg: req.body.name
	};
  }
}

方法二:Body 装饰器

import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller({path:'user',version:"1"})
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  create(@Body() body) {
    return {
		code: 200,
		msg: body.name
	};
  }
}

两种方法使用 Postman 发请求均可得到下图结果
Post

获取动态路由

使用 Param 装饰器

import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller({path:'user',version:"1"})
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get(':id')
  findOne(@Param('id') id: string) {
    return {
    	code: 200,
    	msg: id
    }
  }
}

如下图,在 user 之后加上的内容即为 id
动态路由

持续更新…

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值