一、什么是NestJS
//控制反转
//控制反转是一种思想,依赖注入是控制反转的实现
class A {
constructor(b) {
this.b = b;
}
}
const b = new B(); //在外部进行实例化
const a = new A(b); //依赖注入
二、安装NestJS CLI工具
环境检查
//查看node版本
node -v
//查看npm版本
npm -v
//查看pnpm版本
pnpm -v
//全局下载pnpm
npm install -g pnpm
安装@nest/cli
使用npm全局安装@nestjs/cli
npm i -g @nestjs/cli
查看nest版本
nest -v
结果如图:
创建nest项目
//命令行创建nest项目
nest new 【项目名】
VScode扩展下载
1、ESLint
2、Prettier
3、vscode-icons
三、实践
1、创建项目
nest new [项目名]
使用VScode打开创建的项目
cd 【项目名】
code .
启动项目
//直接启动
pnpm start
//启动dev,写业务的时候,会更新
pnpm start:dev
//在浏览器访问localhost:3000,就能看到启动之后的页面
访问3000端口的原因:
2、各文件功能介绍
各个⽂件介绍
- node_modules ——放置项⽬的依赖⽂件 - src ——放置开发的代码⽂件 - app.controller.spec.ts ——测试 app.controller.ts ⽂件的单元测试⽂件 - app.controller.ts ——根控制器⽂件,处理路由的请求和定义路由处理器 - app.module.ts ——根模块⽂件,Nest 应⽤的主要⼊⼝ - app.service.ts ——根服务⽂件,⽤于处理根控制器中的业务逻辑 - main.ts ——应⽤程序的⼊⼝⽂件,⽤于初始化和启动 Nest 应⽤ - test ——测试⽂件,⽤于编写应⽤程序的单元测试和集成测试 - app.e2e-spec.ts ——编写和运⾏ E2E 测试的测试规范⽂件 - jest-e2e.json ——Jest 的 E2E 测试配置⽂件 - eslintrc.js ——eslint配置⽂件 - .gitignore ——git忽略⽂件 - prettierrc ——prettier配置⽂件 - nest-cli.json ——Nest CLI的配置⽂件 - package.json ——定义项⽬的配置和依赖管理 - pnpm-lock.yaml ——锁定项⽬的依赖包版本 - README.MD ——项⽬的说明⽂件 - tsconfig.build.json ——构建⽣产环境的额外配置⽂件 - tsconfig.json ——TypeScript的配置⽂件
1、.eslintrc.js文件
Eastland是一个存在长时间的库,lynching可以帮我们创建
2、.prettierrc文件
定义格式化的配置
3、nest-cli.json文件
添加或覆盖一些特定的配置
4、package.json
在npm或yorn的项目中都能找到,关于节点项目的一些元数据,使用nest-cli运行的脚本或依赖等,当我们使用npm或yorn安装一个包到项目时,就会被添加到dependencies中
5、tsconfig.json
告诉类型完整的编译器所有需要知道的关于如何编译项目、配置等信息
6、tsconfig.build.json
扩展文件
7、src文件夹
实际代码所在
删除上图三个文件
一个模块可以被其他多个模块导入
NestJS的特点之一:允许我们生成原理图,即一个模块。一个模块就是一个原理图
NestJS在终端查看原理图
nest g
nest g命令执行报错,解决笔记见:
nest : 无法加载文件 C:\Users\admin\AppData\Roaming\npm\nest.ps1,因为在此系统上禁止运行脚本。-CSDN博客
如图所示,问题已经解决,但是,并没有原理图展示,因为没有指定原理图
使用nest g --help可以查看可用原理图列表
nest g --help
//提供模块名字,查看原理图
nest g module tasks
执行上述命令
创建一个新的子文件夹,tasks
app.module.ts文件更新
再次运行项目,确保无误。
快速创建接口服务
nest g res 【服务名】
选中需要的风格,即可创建出该服务
具体学习:
装饰器
装饰器分为:类装饰器、属性装饰器、方法装饰器、参数装饰器
类装饰器
属性装饰器
const doc1:PropertyDecorator = (target:any, propertyKey:string|symbol):void => { console.log(target,propertyKey); } class H2Lclass { @doc1 // 属性装饰器,将修饰的属性打印出来 public title:string; constructor(){ this.title = "H2L,无人知晓!"; } }
方法装饰器
const doc2:MethodDecorator = (target:any, propertyKey:string|symbol, descriptor:any):void => { console.log(target,propertyKey,descriptor); } class H2HLclass { public title:string; constructor(){ this.title = "H2L,无人知晓!"; } @doc2 //方法修饰器 getTitle(){} }
参数装饰器
const doc3:ParameterDecorator = (target:any, propertyKey: string | symbol | undefined, parameterIndex: number):void => { console.log(target,propertyKey,parameterIndex); } class HX2HLclass { public title:string; constructor(){ this.title = "H2L,无人知晓!"; } //在函数中接受两个参数 getTitle(title:string, @doc3 length:number){} }
Nestjs控制器中的装饰器
NestJS控制器中的装饰器
Controllers
负责处理传入的请求并将相应返回给客户端,会绑定一个特殊的路径。
包含处理程序,这些处理程序将处理端点和某些请求方法,比如get、post、delete等
控制器可以利用依赖性注入的优势,在同一模块中消费供应商或服务
如何定义一个控制器?
通过装饰一个类来实现,该装饰器接受一个字符串,即是控制器的处理路径。
@Controller('/tasks')
export class TasksController {
// ....
}
如何在控制器中找到一个处理程序?
在控制器类中有一些简单方法,用装饰器进行装饰,如@get、@post、@delete等,等代表他们的处理方法
@Controller('/tasks')
export class TasksController {
@Get()
getAllTasks() {
return ...;
}
@Post()
createTask() {
return ...;
}
}
创建一个控制器
//不需要规格文件
nest g controller tasks --no-spec
Providers
提供一个模块,以便于使用,或者说可以注入的
可以从Amadeu导出,然后对导入该模块的其他模块使用
如何理解 Nestjs 的提供者 Providers
providers可以将其视为⼀个服务容器,通过 providers 可以提供各种服务,也就是可重复使⽤的代码块,⽤于处 理应⽤程序中的特定任务和逻辑在代码中就是⽤ @Injectable() 装饰器注释的类
Service
什么是服务?
服务是提供者(providers)实现的,但不是所有的providers都是服务
服务是软件开发中的一个常见概念,并不是Nestjs、JavaScript
服务可以被实现为单子,这是一种设计模式,是可注入的,装饰器包装后提供给一个模块,意味着一个实例可以在整个应用中共享
大部分重业务逻辑将驻留在一个服务中
注入
任何组件都可以注入一个用可注入装饰器装饰的提供者,在类的构造函数中定义依赖关系,处理注入
NestJS提供的Get和Post请求
get请求
//Get请求
//控制器
@Get('get')
getHello():string {
return this.appService.getHello();
}
//服务
getHello(): string {
return 'Hello,World!';
}
post请求
//Post请求
//控制器
@Post('post')
postHello():string {
return this.appService.postHello();
}
//服务
postHello(): string {
return 'Hello,World!';
}
Nest Cli常用命令
使用Nest Cli命令提高开发效率
查看所有快捷命令
//查看所有快捷命令 nest --help //生成模块 nest g mo 【模块名】 //生成控制器 nest g co 【控制器名】 //生成服务 nest g s 【服务名】 //生成用户相关接口 nest g resource user //快捷生成模块 nest g res 【模块名】 选择风格,一般选REST API即可
Nestjs RESful风格API开发
RESful
是一种风格,不是标准,也不是协议,主要使用Get和Post完成接口开发
一般接口,调用不同的接口
注册接口,需要再数据库增加用户信息,post方法
删除接口,需要在数据库删除用户信息,post方法
修改接口,需要在数据库修改用户信息,post方法
查询接口,需要在数据库查询用户信息,get方法
RESful接口,调用相同接口的不同方法
查询接口,GET方法
增加接口,POST方法
更新接口,PUT PATCH方法
删除接口,DELETE方法
版本控制
在main.ts文件中,加入内容
//版本控制
app.enableVersioning({
type:VersioningType.URI,
});
在user.controller.ts中改写@Controller中的内容
/*
原本@Controller中的内容为
@Controller('user')
*/
//改写为
@Controller({
path:'user',
version:'1',
})
//以上代码的意思为,路由为user,版本为1
Moudle模块
模块 Module定义在 Nest 中,模块( Module )是组织和管理应⽤程序功能的⼀种⽅式它可以看作是⼀个容器,⽤于将相关的组件、提供者和路由等聚合在⼀起通过模块,我们可以将应⽤程序拆分成更⼩的可重⽤组件,并将其组织成⼀个整体共享模块(需要共享的服务需要抛出)import { Module, Global } from '@nestjs/common'; import { CourseService } from './course.service'; import { CourseController } from './course.controller'; @Global() @Module({ controllers: [CourseController], providers: [CourseService], exports: [CourseService], }) export class CourseModule {}
全局模块import { Module, Global } from '@nestjs/common'; import { CourseService } from './course.service'; import { CourseController } from './course.controller'; @Global() @Module({ controllers: [CourseController], providers: [ CourseService, { provide: 'globalUrl', useValue: { baseUrl: 'https:xdclass.net' } }, ], exports: [ CourseService, { provide: 'globalUrl', useValue: { baseUrl: 'https:xdclass.net' } }, ], }) export class CourseModule {} // 在需要使⽤的控制器中进⾏注⼊ @Inject('globalUrl') private readonly globalUrl: any,
动态模块import { Module, Global, DynamicModule } from '@nestjs/common'; import { CourseService } from './course.service'; import { CourseController } from './course.controller'; interface Options { path: string; } @Global() @Module({}) export class CourseModule { static forRoot(options: Options): DynamicModule { return { module: CourseModule, controllers: [CourseController], providers: [ CourseService, { provide: 'globalUrl', useValue: { baseUrl: `https:xdclass.net${options.path}` }, }, ], exports: [ CourseService, { provide: 'globalUrl', useValue: { baseUrl: `https:xdclass.net${options.path}` }, }, ], }; } }
Nestjs 中间件的使用
中间件
定义请求到达处理程序之前或之后,对请求和响应进⾏处理,类似 express 中间件中间件可以⽤于执⾏各种任务,例如身份验证、⽇志记录、数据转换等
⾃定义依赖注⼊中间件import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; @Injectable() export class Test implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log('测试中间件执⾏'); next(); // res.send('拦截了') } }
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { UserService } from './user.service'; import { UserController } from './user.controller'; import { Test } from '../middleware/Test'; @Module({ controllers: [UserController], providers: [UserService], exports: [UserService], }) export class UserModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(Test).forRoutes('user'); // consumer.apply(Test).forRoutes({ path: 'user', method: RequestMethod.GET }); // consumer.apply(Test).forRoutes(UserController); } }
⾃定义全局中间件import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { Request, Response, NextFunction } from 'express'; const passPath = ['/user']; function middleWareAll(req: Request, res: Response, next: NextFunction) { console.log(req.originalUrl, '全局中间件输出的'); if (passPath.includes(req.originalUrl)) { next(); } else { res.send('user 接⼝才能请求'); } } async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(middleWareAll); await app.listen(3000); } bootstrap();
第三⽅中间件 corsimport { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { Request, Response, NextFunction } from 'express'; import * as cors from 'cors'; const passPath = ['/user']; function middleWareAll(req: Request, res: Response, next: NextFunction) { console.log(req.originalUrl, '全局中间件输出的'); if (passPath.includes(req.originalUrl)) { next(); } else { res.send('user 接⼝才能请求'); } } async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(cors()); app.use(middleWareAll); await app.listen(3000); } bootstrap();
Nest ⾃带跨域配置app.enableCors()
Nestjs上传文件
下载依赖
pnpm i multer @types/multer
import { Module } from '@nestjs/common';
import { UploadService } from './upload.service';
import { UploadController } from './upload.controller';
import { MulterModule } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { join, extname } from 'path';
@Module({
imports: [
MulterModule.register({
storage: diskStorage({
destination: join(__dirname, '../images'),
filename: (_, file, callback) => {
const fileName = `${
new Date().getTime() + extname(file.originalname)
}`;
return callback(null, fileName);
},
}),
}),
],
controllers: [UploadController],
providers: [UploadService],
})
export class UploadModule {}
// 控制器
@Post('img')
@UseInterceptors(FileInterceptor('file'))
upload(@UploadedFile() file) {
console.log(file);
return true;
}
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.useStaticAssets(join(__dirname, 'images'), {
prefix: '/xd',
});
app.use(cors());
app.use(middleWareAll);
await app.listen(3000);
}
bootstrap();
Rxjs
定义RxJS 是⼀种功能强⼤的响应式编程库,使⽤的是观察者模式,简洁⽽直观的⽅式处理异步操作和事件流在 Nest 框架中⾃带的,⽆需安装Rxjs 核⼼功能发布可观察的任务subscribe 订阅任务interval 操作符:不停⽣成值的操作符take 操作符:限制⽣成值的数量of 操作符:⾃定义数据
Nest响应拦截器
响应拦截器⽤于全局处理 HTTP 响应的中间件,提供了⼀种统⼀的⽅式来处理和修改从后端返回给前端的数据例如对响应数据进⾏格式化、加密或⽇志记录{ message: '请求成功了' data: {token:'xxxxx'}, code: 0, }
import type { CallHandler, NestInterceptor } from '@nestjs/common'; import { Injectable } from '@nestjs/common'; import type { Observable } from 'rxjs'; import { map } from 'rxjs'; /** * 拦截器 成功返回的统⼀响应格式 */ @Injectable() export class Response implements NestInterceptor { intercept(context, next: CallHandler): Observable<any> { return next.handle().pipe( map((data) => { return { data, code: 0, }; }), ); } }
Nest异常拦截器
异常拦截器⽤于全局处理 HTTP 异常的中间件,提供了⼀种统⼀的⽅式来处理和修改从后端返回给前端的错误例如统⼀错误格式、向客户端发送适当的错误响应{ code: -1, data: null, msg: message, }
import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common' import { Catch, HttpException } from '@nestjs/common' /** * 异常过滤器 异常返回统⼀响应 * 捕获作为HttpException类实例的异常,并为它们设置⾃定义响应逻辑 */ @Catch(HttpException) export class AbnormalFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { // 获取请求上下⽂ const ctx = host.switchToHttp() // 获取请求上下⽂中的 response对象 const response = ctx.getResponse() // 获取异常状态码 const status = exception.getStatus() // 异常消息 const message = exception.message ? exception.message : 'Service Error' // Response.json()⽅法,使⽤ Response对象直接控制发送的响应。 response.status(status).json({ code: -1, data: null, msg: message, }) } }
Nest的管道PIpe转换
Pipe 的类型转换ParseIntPipe 数据类型@Get(':id') findOne(@Param('id', ParseIntPipe) id: number) { console.log(typeof id); return this.userService.findOne(+id); }
ParseFloatPipe 浮点类型ParseBoolPipe boolean 类型@Get(':id') findOne(@Param('id', ParseBoolPipe) id: boolean) { console.log(typeof id); return this.userService.findOne(+id); }
ParseArrayPipe 数组ParseUUIDPipe uuidParseEnumPipe 枚举DefaultValuePipe 默认值
Nest的管道验证DTO
创建DTO文件
// create-user.dto.ts
import { IsString, IsEmail, Length } from 'class-validator';
export class CreateUserDto {
@IsString()
@Length(3, 20)
name: string;
@IsEmail()
email: string;
@IsString()
@Length(6, 20)
password: string;
}
// main.ts
app.useGlobalPipes(new ValidationPipe());
// user.controller.ts
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
3、Nest ORM框架和数据库
本地安装Docker
网址:https://www.docker.com/get-started/
Nest通过TypeORM链接数据库
TypeORM安装(以MySQL为例)
pnpm i typeorm @nestjs/typeorm mysql2
链接数据库
// config.ts文件 import type { TypeOrmModuleOptions } from '@nestjs/typeorm' export const typeOrmConfig: TypeOrmModuleOptions = { type: 'mysql', host: '127.0.0.1', port: 3306, username: 'root', password: 'xdclass.net168', database: 'xdclass-nest', // 创建的数据库名字为准,⽽不是容器名 entities: ['dist/**/*.entity{.ts,.js}'], autoLoadEntities: true, synchronize: true, }
// app.module.ts文件 imports: [ TypeOrmModule.forRoot(typeOrmConfig), ]
TypeORM配置实体和操作数据库
实体entity
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number = 0; @Column() name: string = ''; @Column() phone: string = ''; @Column() password: string = ''; }
配置
// user.module.ts文件 import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; imports: [TypeOrmModule.forFeature([User])]
// user.controller.ts文件 import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './entities/user.entity'; @InjectRepository(User) private readonly userRepository: Repository<User>,
TypeORM官网
英⽂官⽹: https://typeorm.io/中⽂官⽹: https://www.typeorm.org/