哪里网站建设联系方式,drupal与wordpress哪个容易,网站开发中定义路由的作用,网站收录不好距离上次认真花时间写作#xff0c;似乎已经过了许久许久#xff0c;前端讲了一个新框架 #xff0c;叫 Nest.js 下方是课件#xff0c;有过一定开发经验可跟随视频学习
B站 地址 #xff1a; https://www.bilibili.com/video/BV1Lg4y197u1/?vd_sourcead427ffaf8a5c8344…距离上次认真花时间写作似乎已经过了许久许久前端讲了一个新框架 叫 Nest.js 下方是课件有过一定开发经验可跟随视频学习
B站 地址 https://www.bilibili.com/video/BV1Lg4y197u1/?vd_sourcead427ffaf8a5c8344ec0b6ba368461f0 文章目录 NestJs 介绍1.1 什么是 Nest.js1.2 Nest.js 优点1.3. Nest.js 核心概念 学完理解1.4 快速开始 NestJs 基础学习代码生成器常用装饰器使用Nest JS 测试 NextJs 增删改查测试数据安装 所需依赖实现 CURD安装VSCode 插件测试补充分页查询条件查询GraphQL 接口开发实现graphQL 介绍何时使用grphQL操作实例 NestJs 中间件AOP概念Middleware(中间件)MiddlewareConsumer NestJs 异常处理异常过滤器 Nestjs 拦截器案例使用 NestJs Swagger 接口文档Nest js 整合 **Knife4j**NestJs 文件上传本地文件上传与下载OSS 文件上传 Nest Js 项目实战参考 NestJs 介绍 中文官网: https://nestjs.bootcss.com/ 中文社区文档 https://docs.nestjs.cn/ 英文官网: https://docs.nestjs.com/graphql/quick-start 三方文档: https://www.kancloud.cn/juukee/nestjs/2676779 1.1 什么是 Nest.js
Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用的框架。 它使用渐进式 JavaScript构建并完全支持 TypeScript但仍然允许开发者使用纯 JavaScript 进行编码并结合了 OOP面向对象编程、FP函数式编程和 FRP函数式反应式编程的元素。 在幕后Nest 使用强大的 HTTP 服务器框架如 Express默认也可以选择配置为使用 Fastify Nest 在这些常见的 Node.js 框架Express/Fastify之上提供了一个抽象级别但也直接向开发者公开了它们的 API。 这使开发者可以自由使用可用于底层平台的无数第三方模块。
NestJS 是一个基于 Node.js 的服务端应用开发框架完全支持 TypeScript也可以使用纯 JS 开发支持多种编程范式例如 OOP 和 FR底层处理 HTTP 请求还是使用了 Express(默认)也可切换为 Fastify有无数的第三方模块可供使用
1.2 Nest.js 优点
支持 TypeScript使语法更加规范 兼容 Express 中间件Express 是最早出现的轻量级的 node server端框架 层层处理可以约束代码比如何时使用中间件、使用 Guards守卫 等 依赖注入及模块化的思想提供了完整的 MVC 链路使代码结构清晰便于维护
有很多人说Nest js 和 SpringBoot 很类似 其实Nest 就是Node 当中的 SpringBoot 框架能够帮助我们快速构建Web 应用
NestjsSpringBoot1.轻量级更快2.更擅长处理 I/O任务3.单线程,内存使用率低。1.大量成熟组件2.支持多线程3.更容易维护。1.缺少多线程支持2.执行计算量大的任务时表现不佳3.缺少严格的类型检测,更容易出现导致运行时问题。如果使用TypeScript 也会避免此问题)1.高内存使用率2.没用的依赖比较多3.大量的模板代码导致调式变困难。
1.3. Nest.js 核心概念 学完理解
Nest.js的核心概念和设计原则
模块化架构ModularityNest.js使用模块化的方式组织应用程序将功能划分为模块每个模块负责特定的任务。模块提供了一种封装和隔离的机制使代码更具可维护性和可测试性。依赖注入Dependency InjectionNest.js支持依赖注入模式通过注入依赖项来解耦组件之间的依赖关系。依赖注入提供了灵活性和可测试性并促进了代码的重用和可维护性。控制器与路由Controllers and RoutingNest.js使用控制器来处理HTTP请求并使用装饰器和路由来定义请求的处理方法。控制器负责处理输入和输出并将请求转发给相应的服务。提供者Providers提供者是Nest.js应用程序的基本构建块它们是可注入的类用于处理业务逻辑、数据访问、服务调用等任务。提供者可以在模块中定义并通过依赖注入在应用程序中使用。中间件Middleware中间件是在请求和响应之间进行处理的功能组件用于处理请求前、请求后和错误处理等任务。Nest.js中的中间件提供了对请求和响应的灵活处理和修改的机制。过滤器Filters过滤器用于在异常发生时对异常进行全局处理。它们提供了一种集中处理错误和异常的机制可以捕获和转换异常并生成标准化的错误响应。Guards守卫是一种用于权限验证和路由保护的功能组件。它们提供了在路由处理之前进行身份验证和授权的机制以确保只有满足特定条件的请求可以通过。拦截器Interceptors拦截器用于在请求处理过程中进行全局拦截和修改。它们提供了对请求和响应的拦截和修改的机制可以在请求前、请求后和异常发生时进行处理。异常处理Exception HandlingNest.js提供了一种统一的异常处理机制可以捕获和处理应用程序中的异常。通过自定义异常过滤器可以对异常进行全局处理和转换。
控制器 Controller
客户端的请求最终交给 哪个函数 / 模块 都需要通过预先处理直接处理客户端请求路由、方法等的模块称之为 控制器
控制器原理
控制器 —— 接收应用的特定请求 路由机制 —— 控制哪个控制器接收哪些请求 控制器及路由的关系 —— 一个控制器有多个路由不同路由 执行 不同操作 几乎所有的东西都可以被认为是提供者例如service, repository, factory, helper…提供者的本质用 Injectable() 装饰器 注解的简单类 1.4 快速开始
Nestjs 官方提供了一个很好用的脚手架工具可以用来快速创建开发和构建一个 Nestjs 应用。 首先全局安装脚手架工具
npm install -g nestjs/cli查看脚手架版本号
nest -v
9.1.8使用脚手架提供的 new 命令创建项目
nest new [项目名] 后面输入项目名即可
nest new hello-nest命令行中会提示选择一个你想要的包管理工具本文使用 Yarn 等待几分钟即可 等待1-2分钟 使用yarn start 快速启动 启动成功之后就可以看到 访问默认地址了 http://localhost:3000/
项目结构解释
-- dist[目录] // 编译后的目录用于预览项目
-- node_modules[目录] // 项目使用的包目录开发使用和上线使用的都在里边
-- src[目录] // 源文件/代码程序员主要编写的目录
| -- app.controller.spec.ts // 对于基本控制器的单元测试样例
| -- app.controller.ts // 控制器文件可以简单理解为路由文件
| -- app.module.ts // 模块文件在NestJS世界里主要操作的就是模块
| -- app.service.ts // 服务文件提供的服务文件业务逻辑编写在这里
| -- app.main.ts // 项目的入口文件里边包括项目的主模块和监听端口号
-- test[目录] // 测试文件目录对项目测试时使用的目录比如单元测试...
| -- app.e2e-spec.ts // e2e测试端对端测试文件测试流程和功能使用
| -- jest-e2e.json // jest测试文件jset是一款简介的JavaScript测试框架
-- .eslintrc.js // ESlint的配置文件
-- .gitignore // git的配置文件用于控制哪些文件不受Git管理
-- .prettierrc // prettier配置文件用于美化/格式化代码的
-- nest-cli.json // 整个项目的配置文件这个需要根据项目进行不同的配置
-- package-lock.json // 防止由于包不同导致项目无法启动的配置文件固定包版本
-- package.json // 项目依赖包管理文件和Script文件比如如何启动项目的命令
-- README.md // 对项目的描述文件markdown语法
-- tsconfig.build.json // TypeScript语法构建时的配置文件
-- tsconfig.json // TypeScript的配置文件控制TypeScript编译器的一些行为 src目录下的文件说明 src目录是日常工作编写代码的主要目录从基本的目录结构也可以对NestJS编写模式有很好的了解。
-- src[目录] // 源文件/代码程序员主要编写的目录
| -- app.controller.spec.ts // 对于基本控制器的单元测试样例
| -- app.controller.ts // 控制器文件可以简单理解为路由文件
| -- app.module.ts // 模块文件在NestJS世界里主要操作的就是模块
| -- app.service.ts // 服务文件提供的服务文件业务逻辑编写在这里
| -- app.main.ts // 项目的入口文件里边包括项目的主模块和监听端口号import { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;async function bootstrap() {const app await NestFactory.create(AppModule);await app.listen(3000);
}
bootstrap();
NestJs 基础学习
**核心梳理 **
/controllers目录存放控制器文件每个控制器对应一组路由和请求处理方法。控制器处理来自客户端的HTTP请求并返回相应的响应。/modules目录存放模块文件每个模块代表应用程序的一个功能模块或组件。模块是Nest.js应用程序的基本构建块用于组织和管理相关的控制器、服务和其他组件。/services目录存放服务文件服务是一种可注入的类用于处理业务逻辑、数据访问和其他与控制器无关的功能。服务负责处理控制器的业务逻辑通常会被控制器注入使用。main.ts文件应用程序的入口文件通常用于创建Nest.js应用实例并启动服务器。app.module.ts文件根模块文件用于定义应用程序的根模块。根模块负责引入和配置其他模块、控制器、服务等组件。
代码生成器 在一个项目的生命周期中当我们新增特性的时候我们通常需要在应用中添加新的资源。这些资源通常需要我们在每次新增资源的时候进行一些重复操作。
nestjs/cli 是 NestJS 官方提供的一个非常好用的脚手架工具。执行下面的命令看下 nestjs/cli 为开发者提供了哪些好用的命令。
想象一下真实世界中的场景我们需要通过两个CRUD的终端暴露User和Product两个实体。参考最佳实践我们为每个实体进行以下操作。
生成一个模块 (nest g mo) 来组织代码使其保持清晰的界限将相关模块分组生成一个控制器 (nest g co) 来定义CRUD路径或者GraphQL应用的查询和变更生成一个服务 (nest g s) 来表示/隔离业务逻辑生成一个实体类/接口来代表资源数据类型生成数据转换对象或者GraphQL应用输入来决定数据如何通过网络传输帮忙命令 nest -h 可查看 各种使用 方法nest g resource user 生成项目级别,user 代表文件夹名nest g controller 文件名 --no-spec
很多步骤 为了加速执行重复步骤Nest CLI提供了一个生成器schematic(原理)可以自动生成所有的模板文件以减少上述步骤同时让开发者感觉更易用。 该schematic支持生成HTTP控制器微服务控制器GraphQL处理器代码优先或者原理优先以及WebSocket网关等。 停止生成
如果你不想生成测试文件,可以在nest-cli.json中配置
generateOptions: {spec: false}常用装饰器使用
当查看以上装饰器应用于特定对象时我们可以整理成如下表格
装饰器特定对象备注Request()req从请求对象中提取数据Response()res原生的响应对象Res()res简写形式用于注入响应对象Next()next下一个中间件函数Session()req.session与会话相关的数据Param(key) req.params路由参数中提取指定键的数据Body(key) req.body请求体中提取指定键的数据Query(key) req.query查询参数中提取指定键的数据Headers(name) req.headers请求头中提取指定名称的数据Ip()req.ip请求的 IP 地址HostParam()req.hosts主机名参数
路由装饰器Route decorators
import { Controller, Get, Post, Delete, Patch } from nestjs/common;Controller(example)
export class ExampleController {Get()findAll(): string {return Handle GET request;}Post()create(): string {return Handle POST request;}Delete()remove(): string {return Handle DELETE request;}Patch()update(): string {return Handle PATCH request;}
}请求体装饰器Request body decorators:
import { Controller, Post, Body } from nestjs/common;Controller(example)
export class ExampleController {Post()create(Body() data: any): string {// 处理来自请求体的数据console.log(data);return Handle POST request;}
}使用 Res() 注解时可以访问响应对象的各种属性和方法。以下是一些常见的响应对象的方法和属性
res.status(code: number)设置响应的 HTTP 状态码。res.header(key: string, value: string | string[])设置响应头。res.cookie(name: string, value: any, options?: cookie.CookieSerializeOptions)设置 Cookie。res.clearCookie(name: string, options?: cookie.CookieSerializeOptions)清除指定的 Cookie。res.send(body: any)发送响应正文。res.json(body: any)发送 JSON 格式的响应正文。res.redirect(url: string)进行重定向。
import { Controller, Get, Query } from nestjs/common;Controller(example)
export class ExampleController {Get()findAll(Query() query: any): string {// 处理查询参数console.log(query);return Handle GET request;}
}import { Controller, Get, Param } from nestjs/common;Controller(example)
export class ExampleController {Get(:id)findOne(Param(id) id: string): string {// 处理路由参数console.log(id);return Handle GET request;}
}响应装饰器Response decorators
import { Controller, Get, Res } from nestjs/common;
import { Response } from express;Controller(example)
export class ExampleController {Get()findAll(Res() res: Response): void {// 返回响应res.send(Handle GET request);}
}状态码装饰器Status code decorators
import { Controller, Get, HttpCode } from nestjs/common;Controller(example)
export class ExampleController {Get()HttpCode(201)findAll(): void {// 返回状态码为 201}
}这些示例将帮助你理解如何使用这些装饰器来处理请求和响应以及如何从请求中提取数据。记住在使用这些装饰器之前你需要安装并导入 nestjs/common 模块中相应的装饰器和类。 Get(world)getExample(Req() request: Request): string {// 使用 request 对象执行任何必要的操作console.log(request.headers);console.log(request.query);console.log(request.body);return Hello World11!;}Controller(example)
export class ExampleController {Get(example-path)getExampleData(Query(param1) param1: string,Query(param2) param2: number,Headers(authorization) authorization: string,): string {// 进行一些处理逻辑...return Received query parameters: param1${param1}, param2${param2}, authorization header${authorization};}
}// controller - busingessmodule -- module -- NestFactory
Nest JS 测试
在 NestJS 中你可以使用 Jest 进行单元测试和集成测试。要执行测试文件你可以按照以下步骤进行操作
安装 Jest 首先确保你已经在项目中安装了 Jest通常作为开发依赖项。如果没有安装可以使用以下命令安装 Jest
npm install --save-dev jest types/jest或者
yarn add --dev jest types/jest创建测试文件 创建一个与要测试的文件对应的测试文件。通常测试文件的命名规范是 *.spec.ts 或 *.test.ts。编写测试 在测试文件中编写测试代码包括测试用例、断言和期望的结果。你可以使用 Jest 提供的各种功能来编写测试代码例如 Mock、Matchers 等。运行测试 一旦编写了测试代码你可以使用以下命令来运行测试
npm run test或者
yarn test这将启动 Jest 并运行你的测试文件然后显示测试结果。
在 NestJS 中通常你的控制器、服务、拦截器等组件可以进行单元测试你可以对它们的功能进行测试确保其行为符合预期。
除了上述提到的命令之外你也可以通过配置 package.json 中的 scripts 属性来进行更多自定义的测试命令。希望这些步骤可以帮助你在 NestJS 中执行测试文件。
NextJs 增删改查
测试数据 CREATE TABLE users (id int NOT NULL AUTO_INCREMENT COMMENT primary key,name varchar(30) DEFAULT NULL COMMENT user name,age int DEFAULT NULL COMMENT user age,created_at datetime DEFAULT NULL COMMENT created time,updated_at datetime DEFAULT NULL COMMENT updated time,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT11 DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENTuserINSERT INTO users(id, name, age, created_at, updated_at)VALUES(1, John Doe, 25, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(2, Jane Smith, 30, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(3, Michael Johnson, 40, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(4, Emily Davis, 22, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(5, David Wilson, 35, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(6, Sarah Anderson, 28, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(7, Daniel Thompson, 32, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(8, Olivia Martin, 29, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(9, Matthew Roberts, 27, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
INSERT INTO users(id, name, age, created_at, updated_at)VALUES(10, Isabella Clark, 33, 2023-10-26 08:02:44, 2023-10-26 08:02:44);
安装 所需依赖
安装 连接 MYSQL 需要 ORM 框架映射依赖
yarn add nestjs/typeorm typeorm mysql实现 CURD
修改 app.module.ts 补充 数据库连接信息共享模块 import { Module } from nestjs/common;
import { AppController } from ./app.controller;
import { AppService } from ./app.service;
import { UsersModule } from ./user/users.module;
import { TypeOrmModule } from nestjs/typeorm;Module({imports: [TypeOrmModule.forRoot({ // 连接数据库type: mysql, // 数据库类型host: localhost, // 数据库ip地址port: 3306, // 端口username: root, // 登录名password: root, // 密码database: auth, // 数据库名称entities: [__dirname /**/*.entity{.ts,.js}], // 扫描本项目中.entity.ts或者.entity.js的文件synchronize: true, // 定义数据库表结构与实体类字段同步(这里一旦数据库少了字段就会自动加入,根据需要来使用)}),UsersModule // 加载子模块],controllers: [AppController],providers: [AppService],
})export class AppModule {}
创建 实体 ORM 映射关系
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from typeorm;// 表名
Entity({ name: users })
export class User {PrimaryGeneratedColumn()id: number;Column({ length: 30, nullable: true, comment: user name })name: string;Column({ nullable: true, comment: user age })age: number;CreateDateColumn({ name: created_at, type: datetime, comment: created time })createdAt: Date;UpdateDateColumn({ name: updated_at, type: datetime, comment: updated time })updatedAt: Date;
}创建业务Module 创建业务 模块 一个模块代表一个功能 下面我们实现 模块的 请求 和业务 import { Module } from nestjs/common;
import { UsersController } from ./users.controller;
import { UsersService } from ./users.service;
import { TypeOrmModule } from nestjs/typeorm;
import { User } from ./user.entity;Module({imports:[TypeOrmModule.forFeature([User])], // 导入并注册实体controllers: [UsersController],providers: [UsersService]
})
export class UsersModule {}创建业务Service
import { Injectable } from nestjs/common;
import { InjectRepository } from nestjs/typeorm;
import { Repository } from typeorm;
import { User } from ./user.entity;Injectable()
export class UsersService {// 使用InjectRepository装饰器并引入Repository即可使用typeorm的操作constructor(InjectRepository(User)private readonly usersRepository: RepositoryUser,) { }// 获取所有用户数据列表(usersRepository.query()方法属于typeoram)async getList(): PromiseUser[] {return await this.usersRepository.find();}// 通过id查询用户async getUserById(id): PromiseUser {return await this.usersRepository.findOne({where:{id: id}});}// 新增用户async addUser(body): PromiseUser {return await this.usersRepository.save(body);}// 更新用户async updateUser(user): Promisestring {await this.usersRepository.update({ id: user.id }, user);return 更新成功;}// 删除用户async deleteUser(params): Promiseobject {const res await this.usersRepository.delete({ id: params.id });if (res.affected 0) {return {code: 0,data: ,msg: 删除成功};} else {return {code: 0,data: ,msg: 删除失败};}}}创建 路由对应的控制器
import { Controller, Get, Post, Request, Query, Body, Param } from nestjs/common;
import { UsersService } from ./users.service;
import { User } from ./user.entity;Controller(user)
export class UsersController {// this.usersService new UsersService() 等价于 constructor 方式constructor(private usersService: UsersService){}// 通过数据库查询用户listGet(list)getList(): PromiseUser[] {return this.usersService.getList();}// 通过id查询用户Get(getUserById)async getUserById(Query(id) id: string): PromiseUser {const userId: number parseInt(id);return this.usersService.getUserById(userId);}// 增加用户Post(addUser)addUser(Body() body): PromiseUser {return this.usersService.addUser(body);}// 更新用户Post(updateUser)updateUser(Body() body): Promisestring {return this.usersService.updateUser(body);}// 删除用户Post(deleteUser)delUser(Body() body): Promiseobject {return this.usersService.deleteUser(body);}}安装VSCode 插件测试
使用VsCode插件 REST Client 项目根目录创建RESTClient目录demo.http文件 在项目目录下新建 http 文件
通过 # 区分不同请求
GET http://localhost:3000/user/list###GET www.baidu.com HTTP/1.1 补充分页查询
user/users.controller.ts
Get(pageList)async findAll(Query(page) page: number, Query(pagesize) pagesize: number) {const [users, total] await this.usersService.findAll(page, pagesize);return {data: users,total,page,pagesize,};}
user/users.service.ts async findAll(page: number 1, limit: number 10): Promise[User[], number] {const [users, total] await this.usersRepository.findAndCount({skip: (page - 1) * limit,take: limit,});return [users, total];}
条件查询 Get(findByConditions)async findByConditions(Query(name) conditions): PromiseUser[] {return this.usersService.findByConditions(conditions);
}在使用 TypeORM 中的 FindConditions 类型时你可以按照以下方式进行操作
创建查询条件对象的实例并指定条件
const conditions: FindConditionsUser {name: Alice,age: 25,
};在这个示例中FindConditions 被指定为 User 类型意味着条件对象中的属性必须符合 User 实体类的字段。
将条件对象传递给 find 方法来执行查询
const users await this.usersRepository.find(conditions);在这个示例中conditions 对象被传递给 TypeORM 的 find 方法以根据指定的条件查询用户数据。
使用 FindConditions 类型可以使你能够更加类型安全地构建查询条件并且在编译期间就能够发现潜在的错误。这有助于提高代码的可靠性和可维护性。
若你想要使用 Nest.js 和 TypeORM 完成条件查询可以使用 TypeORM 提供的 Query Builder 和 Where 语句来构建复杂的查询条件。
下面是一个使用 Where 语句构建条件查询的示例
import { Injectable } from nestjs/common;
import { InjectRepository } from nestjs/typeorm;
import { Repository, WhereExpression } from typeorm;
import { User } from ./user.entity;Injectable()
export class UsersService {constructor(InjectRepository(User) private readonly usersRepository: RepositoryUser) {}async findByConditions(conditions: PartialUser): PromiseUser[] {return this.usersRepository.find({where: (qb: WhereExpression) {if (conditions.name) {qb.andWhere(user.name :name, { name: conditions.name });}if (conditions.age) {qb.andWhere(user.age :age, { age: conditions.age });}// Add more conditions as needed},});}
}在这个示例中findByConditions 方法接收一个 conditions 参数它是一个 PartialUser 类型的对象其中包含可能的查询条件。使用 where 方法来构建查询条件根据传入的条件值动态地生成 SQL 查询语句。
你可以根据你的需求向 where 方法中添加更多的条件。例如你可以使用 andWhere 方法来添加更多的 AND 条件使用 orWhere 方法添加 OR 条件。
在查询中user 是表的别名是由 TypeORM 自动生成的指向 User 实体。
这里的示例只是一个基本的条件查询示例你可以根据具体需求添加更多的条件来构建复杂的查询逻辑。
GraphQL 接口开发实现
https://baike.baidu.com/item/rest/6330506?fromtitleRESTfulfromid4406165fraladdin Restful API GraphQL REST API 构建在请求方法method和端点endpoint之间的连接上而 GraphQL API 被设计为只通过一个端点即 /graphql始终使用 POST 请求进行查询其集中的 API 如 http://localhost:3000/graphql所有的操作都通过这个接口来执行这会在后面的操作中在展示到。
一句话理解
简单例子后端使用了GraphQL之后数据库查出来A,B,C,D四个字段客户端需要数据的时候你可以随意使用这四个字段的各种组合只要AC字段还是只要ACD字段等等不再需要和服务端交流。
graphQL 介绍
GraphQL是一种用于API的查询语言是由Facebook公司于2012年开发的一种新型的API架构方式。GraphQL旨在提高客户端应用程序的数据获取效率通过定义数据的类型和结构使得API更加灵活和可扩展。与传统的API不同GraphQL允许客户端指定需要哪些数据从而减少了不必要的数据传输和处理提高了API的效率和可用性。 GraphQL的核心思想是用一个API来代替多个API通过GraphQL API客户端可以获取所需的所有数据而不需要调用多个API或者进行多次请求。GraphQL还支持实时数据查询和订阅使得客户端可以实时获取数据更新从而更好地支持实时应用程序。 Nest js 网址 https://nestjs.bootcss.com/graphql/quick-start.html graphQL官方地址 https://graphql.org/ Graph”指的是数据以类似图形的结构表示和连接。在GraphQL中数据表示为具有节点和边的图形允许不同数据实体之间的灵活关系。 “QL”代表“查询语言”因为GraphQL提供了一种精确和可控的查询或请求数据的语言。 示例书和作者的关系可以用图来表示可以很容易地使用GraphQL进行查询 何时使用grphQL
GraphQL适用于处理复杂或经常变化的数据需求因为它可以将数据请求的控制权交给客户端让客户端在任何时间请求任何数据。这让在每次API变更迭代或从这些API请求的数据发生变化时更容易进行更新。与REST API相比GraphQL允许客户端精确指定所需数据的结构和字段从而避免了获取过度或获取不足的问题。因此GraphQL可以提高数据传输的精确性和效率使应用程序更加灵活和可扩展。 如果应用程序需要从多个来源聚合数据GraphQL可以帮助把这些来源统一到单个API中。它提供了一个抽象层可从各种服务中获取和组合数据使数据的获取和整合更加简单和高效。 当应用程序需要实时更新和订阅时GraphQL提供内置功能。这些功能允许客户端订阅特定数据的变化并在数据更改时实时推送通知从而提高应用程序的实时性和响应性。 总之如果您需要更好地控制和定制API响应并希望有效地处理复杂或不断变化的数据需求那么GraphQL是一个非常有用的工具。
操作实例
安装 grphQL
Apollo是一个开源的JavaScript库用于构建基于GraphQL的客户端应用程序。它提供了一组工具和功能帮助开发人员在前端应用中轻松集成和管理GraphQL API。 主要功能包括
查询和变更管理Apollo提供了一套强大的工具用于管理应用中的GraphQL查询、变更和订阅。缓存管理Apollo维护一个本地缓存用于存储从GraphQL API中获取的数据。这样当下次需要相同数据时可以直接从缓存中获取提高了应用的性能和响应速度。数据更新和响应处理Apollo可以自动更新UI组件中的数据同时提供了一些功能来处理后端数据的变更和响应。查询优化Apollo的缓存机制和数据更新处理功能可以优化GraphQL查询避免重复请求和传输不必要的数据。错误处理和状态管理Apollo提供了错误处理和状态管理的功能帮助开发人员更好地处理和呈现GraphQL API返回的错误信息。
Apollo提供了适用于不同JavaScript框架的客户端库例如React、Angular、Vue等。它使开发人员能够轻松地在前端应用程序中使用GraphQL并提供了许多工具和便利功能简化了与GraphQL API的交互和管理。 总的来说Apollo是一个功能强大的GraphQL客户端库
npm i nestjs/graphql nestjs/apollo apollo/server graphql在根目录进行配置
import { Module } from nestjs/common;
import { AppController } from ./app.controller;
import { AppService } from ./app.service;
import { GraphQLModule } from nestjs/graphql;
import { ApolloDriverConfig,ApolloDriver } from nestjs/apollo
import { join } from path;
import { UsersModule } from ./users/users.module;Module({imports: [GraphQLModule.forRootApolloDriverConfig({driver: ApolloDriver,autoSchemaFile: join(process.cwd(), src/schema.gql),}),UsersModule,],controllers: [AppController],providers: [AppService],
})
export class AppModule {}
快速生成CRUD
nest g res修改测试请求
import { Resolver, Query, Mutation, Args, Int } from nestjs/graphql;
import { UsersService } from ./users.service;
import { User } from ./entities/user.entity;
import { CreateUserInput } from ./dto/create-user.input;
import { UpdateUserInput } from ./dto/update-user.input;Resolver(() User)
export class UsersResolver {constructor(private readonly usersService: UsersService) {}Mutation(() User)createUser(Args(createUserInput) createUserInput: CreateUserInput) {return createUserInput;}// 根据返回类型进行输入Query(() [User], { name: userList,description:用户列表})findAll() {return this.usersService.findAll();}Query(() User, { name: user })findOne(Args(id, { type: () Int }) id: number) {return this.usersService.findOne(id);}Mutation(() User)updateUser(Args(updateUserInput) updateUserInput: UpdateUserInput) {return updateUserInput;}Mutation(() Boolean)removeUser(Args(id, { type: () Int }) id: number) {return this.usersService.remove(id);}
}
业务层
import { Injectable } from nestjs/common;
import { CreateUserInput } from ./dto/create-user.input;
import { UpdateUserInput } from ./dto/update-user.input;
import { User } from ./entities/user.entity;Injectable()
export class UsersService {create(createUserInput: CreateUserInput) {return new User;}findAll() {return [{exampleField:1},{exampleField:2},{exampleField:3}];}findOne(id: number) {return {exampleField:id};}update(id: number, updateUserInput: UpdateUserInput) {return This action updates a #${id} user;}remove(id: number) {return true}
}
测试 步骤
query {findAllstudent {exampleField}
}查询
# Write your query or mutation here
{#根据ID 查询 实际调用name函数名user(id:2){exampleField}#查询全部userList{exampleField}}新增和修改
mutation{# 删除removeUser(id:1)# 修改updateUser(updateUserInput: { exampleField: 1, id: 1 }) {exampleField}# 新增createUser(createUserInput: { exampleField: 1 }) {exampleField}}mutation{# 增删改createStudent(# 参数对象的返回 createStudentInput:{exampleField:1}){# 大括号是方法的返回exampleField},updateStudent(# 参数对象的返回 updateStudentInput:{exampleField:1,id:1}){# 大括号是方法的返回exampleField}}对应的 schema 结构
type User {# 示例属性exampleField: Int!
}type Query {# 用户列表userList: [User!]!user(id: Int!): User!
}type Mutation {createUser(createUserInput: CreateUserInput!): User!updateUser(updateUserInput: UpdateUserInput!): User!removeUser(id: Int!): Boolean!
}input CreateUserInput {# Example field (placeholder)exampleField: Int!
}input UpdateUserInput {# Example field (placeholder)exampleField: Intid: Int!
}
NestJs 中间件 AOP概念
在介绍 AOP 架构之前我们需要先了解一下 NestJS 对一个请求的处理过程。在NestJS中,一个请求首先会先经过控制器Controller,然后 Controller 调用服务 (Service)中的方法,在 Service 中可能还会进行数据库的访问(Repository)等操作,最后返回结果。但是如果我们想在这个过程中加入一些通用逻辑,比如打印日志,权限控制等该如何做呢?这时候就需要用到 AOP(Aspect-Oriented Programming面向切面编程)了,它允许开发者通过定义切面Aspects来对应用程序的各个部分添加横切关注点Cross-Cutting Concerns。横切关注点是那些不属于应用程序核心业务逻辑但在整个应用程序中多处重复出现的功能或行为。这样可以让我们在不侵入业务逻辑的情况下来加入一些通用逻辑。
Middleware(中间件)
Middleware is a function which is called before the route handler. Middleware functions have access to the request and response objects, and the next() middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next. 中间件学过Express和Koa的同学对中间件这个概念应该很熟悉了。看官方说明中间件可以拿到Request、Response对象及next函数其实nest默认和express的中间件是等效的 再来回忆一下中间件的功能特性
可以执行任意的代码对request和response对象进行改造结束request-response循环通过next()调用下一个中间件如果当前中间件没有结束当前request-response循环必须调用next()函数否则请求会处于挂起状态阻塞整个应用
然后再生成一个中间件
nest g mi test --no-spec --flat此时我们的根目录多了一个test.middleware.ts文件,因为我们的NestJS是基于Express的,所以可以将req和res的类型设置成Express中的类型
import { Injectable, NestMiddleware } from nestjs/common;
import { Request, Response } from express;
Injectable()
export class TestMiddleware implements NestMiddleware {use(req: Request, res: Response, next: () void) {console.log(请求进入前);next();console.log(请求离开后);}
}其中next()就是执行下一个中间件,而next()后面的代码则是请求结束后才会执行的。那么我们如何使用这个中间件呢? 想要使用中间件需要到app.module.ts进行注册
import { MiddlewareConsumer, Module, NestModule } from nestjs/common;
import { AppController } from ./app.controller;
import { AppService } from ./app.service;
import { TestMiddleware } from ./test.middleware;
Module({imports: [],controllers: [AppController],providers: [AppService],
})export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {consumer.apply(TestMiddleware).forRoutes(*);}}我们需要实现一个NestModule的类然后通过其中的configure方法进行使用。启动项目访问一下http://localhost:3000/看一下控制台的打印
注意configure方法可以是异步的如果里面有需要异步处理的操作可以使用async/await来等待操作完成再往下进行
MiddlewareConsumer
实现链式调用apply可以放置多个middlewareforRoutes可以使用单个string路径多个string路径RouteInfo对象单个Controller多个Controller
Module({imports: [],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {consumer.apply(LoggerMiddleware, xxxMiddleware,...).forRoutes(CatsController,UserController,...);}
}exclude可以排除不使用中间件的路径
consumer.apply(LoggerMiddleware).exclude({ path: cats, method: RequestMethod.GET },{ path: cats, method: RequestMethod.POST },cats/(.*),).forRoutes(CatsController);
在Nest.js中你可以使用全局中间件和局部中间件来处理请求和响应。全局中间件将对应用程序中的每个路由生效而局部中间件只对特定的路由或模块生效。
以下是全局中间件和局部中间件的使用示例
**全局中间件 **全局中间件会在应用程序的每个请求处理之前执行可以使用app.use()方法在根模块中注册全局中间件。
// logger.middleware.tsimport { Injectable, NestMiddleware } from nestjs/common;
import { Request, Response } from express;Injectable()
export class LoggerMiddleware implements NestMiddleware {use(req: Request, res: Response, next: Function) {console.log(Request...);next();}
}// main.tsimport { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;
import { LoggerMiddleware } from ./logger.middleware;async function bootstrap() {const app await NestFactory.create(AppModule);app.use(LoggerMiddleware);await app.listen(3000);
}
bootstrap();局部中间件 局部中间件只对特定的路由或模块生效可以通过在路由处理程序上使用UseMiddleware()装饰器或在模块中使用configure()方法来注册局部中间件。
// logger.middleware.tsimport { Injectable, NestMiddleware } from nestjs/common;
import { Request, Response } from express;Injectable()
export class LoggerMiddleware implements NestMiddleware {use(req: Request, res: Response, next: Function) {console.log(Request...);next();}
}// cats.controller.tsimport { Controller, Get } from nestjs/common;
import { UseMiddleware } from nestjs/common;
import { LoggerMiddleware } from ./logger.middleware;Controller(cats)
UseMiddleware(LoggerMiddleware)
export class CatsController {Get()findAll(): string[] {return [Cat 1, Cat 2];}
}UseGuards 或 UseFilters 等装饰器来应用中间件。
// app.module.tsimport { Module } from nestjs/common;
import { CatsModule } from ./cats/cats.module;Module({imports: [CatsModule]
})
export class AppModule {}在上面的示例中LoggerMiddleware被用作局部中间件它只会应用于CatsController的路由处理程序。这意味着每次访问/cats路径时都会执行LoggerMiddleware中间件的逻辑。
全局中间件和局部中间件的使用场景取决于具体的需求。全局中间件通常用于实现通用的请求处理逻辑如日志记录和身份验证。而局部中间件则可以更精确地定义中间件应用的位置和范围适用于特定的路由或模块。
通过选择合适的中间件注册方式可以更加灵活地处理请求和响应并增强Nest.js应用程序的功能和性能。请根据你的实际需求选择适合的中间件注册方式。
第三方中间件
在Nest.js中你可以轻松地使用第三方中间件来处理请求和响应。以下是一个使用第三方中间件的示例以及如何在Nest.js中使用它
安装第三方中间件 在使用第三方中间件之前需要使用npm或yarn等包管理工具安装它。以**helmet**中间件为例可以运行以下命令安装它
helmet是一个安全中间件用于增强 Express 或 Connect 应用程序的安全性。它帮助解决了许多常见的 Web 应用程序安全漏洞如跨站点脚本攻击 (XSS)、点击劫持、HTTP 头部设置不当等。
helmet中间件提供了一组对安全性有益的 HTTP 头部设置例如适当的内容安全策略、XSS 过滤、CSRF 保护等。通过将这些头部设置在服务器响应中发送给客户端可以提供一定程度的保护防止常见的攻击。
在Nest.js中可以使用第三方中间件库nest-middlewares提供的HelmetMiddleware来使用helmet中间件。在应用程序中将HelmetMiddleware应用到所有路由时会自动添加并设置helmet中间件所需的安全头部。
helmet中间件可以大大简化安全性的处理并提供一些默认的安全设置。然而对于更严格的安全需求你可能需要更深入地了解和配置具体的安全措施。
请注意虽然helmet中间件能够提供一定程度的安全保护但应该将其视为安全加固措施的一部分而不是唯一的安全措施。仍然建议结合其他安全措施和最佳实践来确保应用程序的安全性。
在上面的示例中我们使用了**nest-middlewares**库中的**HelmetMiddleware**中间件。通过导入**HelmetMiddleware**模块并将其应用到**forRoutes(*)**我们可以将**helmet**中间件应用于所有的路由。
请注意不同的第三方中间件可能需要不同的配置选项。你可以查看相应中间件的文档以获取更多详细信息并根据需要进行适当的配置。
通过使用第三方中间件你可以轻松地增强你的Nest.js应用程序的功能和安全性。可以根据你的需求选择适合的第三方中间件并按照上述示例进行使用和配置。同时确保在使用第三方中间件之前阅读它们的文档以获取更多详细信息。
很抱歉我的回答中提到了错误的库。在 Nest.js 中使用 Helmet 中间件时你可以直接安装和使用 helmet 库而不是 nest-middlewares/helmet。
可以使用以下命令来安装 **helmet**
npm install helmet然后在你的 Nest.js 应用程序中使用 **helmet** 中间件的代码示例如下
import { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;
import helmet from helmet;async function bootstrap() {const app await NestFactory.create(AppModule);app.use(helmet());await app.listen(3000);
}
bootstrap();在这个示例中我们首先通过 **import helmet from helmet;** 导入 **helmet** 中间件。然后我们在应用程序实例上使用 **app.use(helmet());** 将 **helmet** 中间件应用到所有路由上。
请注意如果你使用的是 TypeScript需要确保 **esModuleInterop** 设置为 **true**并确保你的项目中已经正确配置了 TypeScript。
很抱歉我的回答中提到了错误的库。在 Nest.js 中使用 Helmet 中间件时你可以直接安装和使用 helmet 库而不是 nest-middlewares/helmet。
全局使用示例
然后在你的 Nest.js 应用程序中使用 helmet 中间件的代码示例如下
import { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;
import helmet from helmet;async function bootstrap() {const app await NestFactory.create(AppModule);app.use(helmet());await app.listen(3000);
}
bootstrap();在这个示例中我们首先通过 import helmet from helmet; 导入 helmet 中间件。然后我们在应用程序实例上使用 app.use(helmet()); 将 helmet 中间件应用到所有路由上。
请注意如果你使用的是 TypeScript需要确保 esModuleInterop 设置为 true并确保你的项目中已经正确配置了 TypeScript。
NestJs 异常处理 异常过滤器
Nest 带有一个内置的异常层负责处理应用程序中所有未处理的异常。当应用程序代码未处理异常时该层会捕获该异常然后自动发送适当的用户友好响应。
nest g f http-exception --no-spec --flat通过使用 Nest CLI 提供的快捷命令你可以方便地生成异常类并快速开始在你的应用程序中使用它们。这样你可以避免手动创建异常类的过程并节省时间。
在 Nest.js 中异常过滤器是一种处理应用程序中抛出的异常的机制。异常过滤器允许我们捕获并处理应用程序中的异步代码中出现的异常。使用异常过滤器我们可以自定义如何处理异常例如向客户端返回特定的错误响应。
要使用异常过滤器可以按照以下步骤操作
创建一个自定义的异常过滤器类。这个类应该实现 ExceptionFilter 接口。
import { Catch, ArgumentsHost } from nestjs/common;
import { BaseExceptionFilter } from nestjs/core;Catch()
export class CustomExceptionFilter extends BaseExceptionFilter {catch(exception: any, host: ArgumentsHost) {// 自定义异常处理逻辑console.error(发生异常, exception);super.catch(exception, host);}
}在上面的例子中我们创建了一个名为 CustomExceptionFilter 的自定义异常过滤器类。我们覆盖了 catch 方法可以在其中定义我们自己的异常处理逻辑。
假设你想通过 Nest.js 提供的内置的 HTTPException 类捕获 HTTP 异常。你可以在 catch 方法中添加逻辑来处理 HTTP 异常。
import { Catch, ArgumentsHost } from nestjs/common;
import { BaseExceptionFilter, HttpException } from nestjs/core;
import { Response } from express;Catch(HttpException)
export class HttpExceptionFilter extends BaseExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const context host.switchToHttp();const response context.getResponseResponse();const status exception.getStatus();response.status(status).json({statusCode: status,message: exception.message,});}
}最佳实践
nest g f http-exception --no-spec --flat并将这个过滤器http-exception.filter.ts修改如下
import { ExceptionFilter, Catch, ArgumentsHost } from nestjs/common;
import { HttpException } from nestjs/common;Catch(HttpException)export class HttpExceptionFilter implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx host.switchToHttp(); // 获取请求上下文const response ctx.getResponse(); // 获取 response 对象const request ctx.getRequest(); // 获取 request 对象const status exception.getStatus(); // 获取异常的状态码response.status(status).json({statusCode: status,timestamp: new Date().toISOString(),path: request.url,});}}如果想要全局使用,可以在main.ts中使用app.useGlobalFilters进行注册
import { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;import { HttpExceptionFilter } from ./http-exception.filter;
async function bootstrap() {const app await NestFactory.create(AppModule);app.useGlobalFilters(new HttpExceptionFilter());await app.listen(3000);
}
bootstrap();或者在app.module注入
import { Module } from nestjs/common;
import { APP_FILTER } from nestjs/core;
import { HttpExceptionFilter } from ./http-exception.filter;
Module({providers: [{provide: APP_FILTER,useClass: HttpExceptionFilter,},],
})export class AppModule {}这种方式适合在过滤器需要依赖注入的时候使用 如果只想在单个控制器中使用,可以使用UseFilters装饰器
Get(aa)UseFilters(HttpExceptionFilter)aa(): string {throw new HttpException(这是一个400异常, 400);return aa;}这里我们采用全局注册,此时我们在抛出HttpException异常看下请求结果
可以看到已经是我们异常过滤器中response.status(status).json中传入的数据及格式了 除了http异常状态码之外,通常在开发中还会返回一些业务的异常,对于这些异常的处理我们可以新建一个custom.exception.ts,当然命名大家可以随意
import { HttpException, HttpStatus } from nestjs/common;export enum CustomErrorCode {USER_NOTEXIST 10002, // 用户不存在USER_EXIST 10003, //用户已存在
}
export class CustomException extends HttpException {private errorMessage: string;private errorCode: CustomErrorCode;constructor(errorMessage: string,errorCode: CustomErrorCode,statusCode: HttpStatus HttpStatus.OK,) {super(errorMessage, statusCode);this.errorMessage errorMessage;this.errorCode errorCode;}getErrorCode(): CustomErrorCode {return this.errorCode;}getErrorMessage(): string {return this.errorMessage;}
}这里实现了一个CustomException类继承HttpException类,接收三个参数:错误信息,业务异常码以及http错误码(默认为200,一般业务异常http请求通常是正常的)。同时提供了获取错误码及错误信息的函数。同时导出了一个自定义错误码的枚举 然后我们来到异常过滤器中http-exception.filter.ts做一些逻辑处理
import { ArgumentsHost, Catch, ExceptionFilter,HttpException,NotFoundException } from nestjs/common;
import { CustomException } from ./custom.exception;Catch(HttpException)
export class HttpExceptionFilterT implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx host.switchToHttp(); // 获取请求上下文const response ctx.getResponse(); // 获取 response 对象const request ctx.getRequest(); // 获取 request 对象const status exception.getStatus(); // 获取异常的状态码//判断是否为自定义类if (exception instanceof CustomException) {response.status(status).json({statusCode: status,message: exception.getErrorMessage(),timestamp: new Date().toISOString(),path: request.url,test:自定义异常});return;}response.status(status).json({statusCode: status,timestamp: new Date().toISOString(),path: request.url,message:我是你自定义的异常});}
}
这里通过判断是否是通过自定义异常抛出的错误来返回不同的结果,然后我们在控制层抛出一个自定义错误看一下
import { Controller, Get, HttpException, UseFilters } from nestjs/common;
import { AppService } from ./app.service;
import { CustomException, CustomErrorCode } from ./custom.exception;
import { HttpExceptionFilter } from ./http-exception.filter;
Controller()export class AppController {constructor(private readonly appService: AppService) {}Get(aa)UseFilters(HttpExceptionFilter)aa(): string {throw new CustomException(姓名已存在, CustomErrorCode.USER_EXIST);return aa;}}然后访问/aa接口会发现请求是成功的,并且statusCode变成了我们期望的10003 Nestjs 拦截器
在 Nest.js 中拦截器Interceptors是一种可用于在处理请求之前、之后或处理过程中干预和转换数据的强大机制。它们允许你在执行路由处理程序之前和之后以及在异常抛出时对请求和响应对象进行操作。拦截器有以下几种作用
**日志记录: **拦截器可以用于记录请求和响应数据以便跟踪应用程序的活动、调试问题或分析性能。
**验证和转换数据: **通过拦截器你可以验证请求的有效性转换请求数据或响应数据的格式以适应特定的需求。
权限控制: 拦截器可以用于检查用户的权限、角色或其他访问控制要求以确保只有有权限的用户可以访问特定的路由或资源。
异常处理: 拦截器可以用于捕获和处理异常通过统一处理异常情况你可以避免重复的错误处理逻辑并提供一致的错误响应。
缓存: 拦截器可以用于实现缓存机制将某些请求的响应缓存起来提高系统的性能和响应时间。
使用拦截器可以在整个请求-响应生命周期中执行各种操作,例如:
在请求到达路由处理程序之前对请求进行预处理或验证。 在路由处理程序执行之前或之后对请求和响应进行操作。 在异常发生时捕获并统一处理异常。
通过命令,创建一个拦截器, 在nestjs中拦截器依赖于rxjs实现对数据的转换处理
Interceptor 要实现 Nest Interceptor 接口实现 intercept 方法调用 next.handle() 就会调用目标 Controller可以在之前和之后加入一些处理逻辑。
nest g itc data拦截器代码
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from nestjs/common;
import { Observable } from rxjs;Injectable()
export class DataInterceptor implements NestInterceptor {// 在这里进行拦截在请求到达之前intercept(context: ExecutionContext, next: CallHandler): Observableany {console.log(拦截到达之前)return next.handle();}
}
在controller中使用用法同pipe,guard也可以全局使用
局部使用
import { DataInterceptor } from src/data/data.interceptor;
UseInterceptors(DataInterceptor)全局使用
import { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;
import { LoginMiddleware } from ./login.middleware;
import { HttpExceptionFilter } from ./http-exception.filter;
import { DataInterceptor } from src/data/data.interceptor;
import helmet from helmet;
async function bootstrap() {// main.ts --- Factory -- Moduleconst app await NestFactory.create(AppModule);app.use(helmet());// 所有的请求所有控制器全部使用app.useGlobalInterceptors(new DataInterceptor())app.useGlobalFilters(new HttpExceptionFilter());app.use(new LoginMiddleware().use);await app.listen(3000);
}
bootstrap();
案例使用 在前后端交互的过程中很常见的一个应用场景后端返回的数据的格式比如返回的json数据外面再包一层data当然这个要看个人和公司开发习惯,这样我们可以用拦截器对响应后的数据进行处理 嵌套使用
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from nestjs/common;
import { Observable, map } from rxjs;Injectable()
export class DataInterceptor implements NestInterceptor {// 在这里进行拦截在请求到达之前intercept(context: ExecutionContext, next: CallHandler): Observableany {console.log(拦截到达之前)return next.handle().pipe(map(data({data})))}
} 判断使用
import { BadRequestException, CallHandler, ExecutionContext, Injectable, NestInterceptor } from nestjs/common;
import { Observable, map } from rxjs;
import { CustomException } from src/custom.exception;Injectable()
export class DataInterceptor implements NestInterceptor {// 在这里进行拦截在请求到达之前intercept(context: ExecutionContext, next: CallHandler): Observableany {const req context.switchToHttp().getRequest()if(req.body.password!XXX){throw new BadRequestException()}return next.handle().pipe(map(data({data})))}
}
日志打印使用
我们首先从 中获取请求对象并提取出请求的方法和 URL并打印请求接收的日志。然后调用 方法获取被拦截路由处理后的结果并通过 操作符添加一个回调函数。在回调函数中我们打印响应发送的日志。
import { BadRequestException, CallHandler, ExecutionContext, Injectable, NestInterceptor } from nestjs/common;
import { Observable, map, tap } from rxjs;
import { CustomException } from src/custom.exception;Injectable()
export class DataInterceptor implements NestInterceptor {// 在这里进行拦截在请求到达之前intercept(context: ExecutionContext, next: CallHandler): Observableany {const request context.switchToHttp().getRequest()const {method,url} request;console.log([${method}] ${url} - Request received)return next.handle().pipe(tap((){console.log([${method}] ${url} - Request sent)}))}
}
NestJs Swagger 接口文档
首先您必须安装所需的包
$ npm install --save nestjs/swagger swagger-ui-expressimport { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;
import { LoginMiddleware } from ./login.middleware;
import { HttpExceptionFilter } from ./http-exception.filter;
import { DataInterceptor } from ./data/data.interceptor;
import helmet from helmet;
import { DocumentBuilder, SwaggerModule } from nestjs/swagger;async function bootstrap() {const app await NestFactory.create(AppModule);const config new DocumentBuilder().setTitle(This is a title).setDescription(This is a description).setVersion(1.0).addTag(cat).addTag(dog).build();const document SwaggerModule.createDocument(app, config);SwaggerModule.setup(api, app, document);app.use(helmet());app.useGlobalInterceptors(new DataInterceptor());app.useGlobalFilters(new HttpExceptionFilter());app.use(new LoginMiddleware().use);await app.listen(3000);
}bootstrap();DocumentBuilder 有助于构建符合 OpenAPI 规范的基础文档。它提供了几种允许设置诸如标题描述版本等属性的方法。为了创建一个完整的文档使用已定义的 HTTP 路由我们使用 SwaggerModule 类的 createDocument() 方法。 此方法接收两个参数即应用程序实例和 Swagger 选项对象。 一旦创建完文档我们就可以调用 setup() 方法。 它接收
Swagger UI 的挂载路径应用程序实例上面已经实例化的文档对象
现在您可以运行以下命令来启动 HTTP 服务器
$ npm run start应用程序运行时打开浏览器并导航到 http://localhost:3000/api 。 你应该可以看到 Swagger UI 通过装饰器对接口更详细地说明
import { Controller, Get, Param } from nestjs/common;
import { ApiTags, ApiParam } from nestjs/swagger;ApiTags(dog)
Controller(dog)
export class DogController {Get(/onedog/:name)// 通过ApiParam装饰器来描述接口需要哪些参数ApiParam({ name: name, type: String, description: 姓名 })ApiParam({ name: gender, type: String, description: 性别 })aGetRequest(Param(name) name: string, Param(gender) gender: string): string { return a ${gender} named ${name} has lunched a get request}
} Nest js 整合 Knife4j
官方地址 https://www.npmjs.com/package/nestjs-knife4j?activeTabreadme 平台官方 https://doc.xiaominfo.com/demo/doc.html#/SwaggerModels/Knife4j%E7%A4%BA%E4%BE%8B https://github.com/xiaoymin/knife4j/wiki
import { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;
import { LoginMiddleware } from ./login.middleware;
import { HttpExceptionFilter } from ./http-exception.filter;
import { DataInterceptor } from ./data/data.interceptor;
import helmet from helmet;
import { knife4jSetup } from nestjs-knife4j
import { DocumentBuilder, SwaggerModule } from nestjs/swagger/dist;async function bootstrap() {const app await NestFactory.create(AppModule);const options new DocumentBuilder().setTitle(Cats example).setDescription(The cats API description).setVersion(1.0).addTag(cats).build()const document SwaggerModule.createDocument(app, options)SwaggerModule.setup(api, app, document)knife4jSetup(app, {urls: [{name: 2.X版本,url: /api-json,swaggerVersion: 3.0,location: /api-json,},],})app.use(helmet());app.useGlobalInterceptors(new DataInterceptor());app.useGlobalFilters(new HttpExceptionFilter());app.use(new LoginMiddleware().use);await app.listen(3000);
}bootstrap();访问地址 http://localhost:3000/doc.html NestJs 文件上传
本地文件上传与下载
安装库
yarn add multer types/multer快速创建
nest g res upload注册文件上传的库
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({//里面有register 和 registerAsync 两个方法前者是同步的后者是异步的imports: [MulterModule.register({//图片上传完要存放的位置storage: diskStorage({destination: join(__dirname, ../images),//存放的文件路径filename: (req, file, callback) {//重新定义文件名file.originalname 文件的原始名称// extname 获取文件后缀const fileName ${new Date().getTime() extname(file.originalname)};//返回新的名称参数1是错误这里用null就好return callback(null, fileName)}}),})],controllers: [UploadController],providers: [UploadService]
})
export class UploadModule { }
具体使用文件上传的库 使用文件上传的拦截器
import { Controller, Post,UseInterceptors,UploadedFile,Get,Res,Query} from nestjs/common;
import { UploadService } from ./upload.service;
// FileInterceptor用于单文件上传FilesInterceptor用于多文件上传
import {FileInterceptor,FilesInterceptor} from nestjs/platform-express
import {join} from path;
import type { Response } from express
Controller(upload)
export class UploadController {constructor(private readonly uploadService: UploadService) {}Post(test)// UseInterceptors 处理文件的中间件file是一个标识名UseInterceptors(FileInterceptor(file))// UploadedFile装饰器是用于读取文件的upload (UploadedFile() file) {console.log(file,file)return true}// 文件下载Get(export)download(Res() res: Response, Query(filename) filename: string) {// 正常应该是从数据库里获取地址const url join(__dirname, ../images/${filename});res.download(url);}}
配置文件静态资源访问
import { NestFactory } from nestjs/core;
import { AppModule } from ./app.module;
import { LoginMiddleware } from ./login.middleware;
import { HttpExceptionFilter } from ./http-exception.filter;
import { DataInterceptor } from ./data/data.interceptor;
import helmet from helmet;
import { knife4jSetup } from nestjs-knife4j
import { DocumentBuilder, SwaggerModule } from nestjs/swagger/dist;//用于配置静态文件访问
import { NestExpressApplication } from nestjs/platform-express;
import {join} from path;async function bootstrap() {const app await NestFactory.createNestExpressApplication(AppModule)//配置静态文件访问目录//配置静态文件访问目录app.useStaticAssets(join(__dirname,images),{prefix:/files/})app.useStaticAssets(join(__dirname,..,public),{// 配置虚拟路径prefix:/files/})const options new DocumentBuilder().setTitle(Cats example).setDescription(The cats API description).setVersion(1.0).addTag(cats).build()const document SwaggerModule.createDocument(app, options)SwaggerModule.setup(api, app, document)// knife4j的配置 knife4jSetup(app, {urls: [{name: 测试平台2.X版本,url: /api-json,swaggerVersion: 3.0,location: /api-json,},],})// 注册全局守卫app.use(helmet());// app.useGlobalGuardsapp.useGlobalInterceptors(new DataInterceptor());// app.useGlobalFilters(new HttpExceptionFilter());app.use(new LoginMiddleware().use);//app.use(AuthGuard)await app.listen(3000);
}bootstrap();多文件下载 Get(export)async download(Res() res: Response, Query(filenames) filenames: string) {const fileNamesArray filenames.split(,);const zip new JSZip();const promises [];fileNamesArray.forEach((filename) {const file fs.readFileSync(join(__dirname, ../images/${filename}));zip.file(filename, file);promises.push(zip.generateAsync({ type: nodebuffer }));});const buffers await Promise.all(promises);const zipFilename multifiles.zip;const fullZipPath path.join(__dirname, ../zip, zipFilename);fs.writeFileSync(fullZipPath, Buffer.concat(buffers));res.download(fullZipPath, zipFilename, (err) {if (err) {console.log(err);} else {fs.unlinkSync(fullZipPath);}});}测试 OSS 文件上传
要在 Nest.js 中实现文件上传到阿里云 OSS 的功能你可以按照以下步骤进行操作
步骤 1: 安装依赖 首先确保你已经在项目中安装了 nestjs/oss 和 ali-oss 这两个依赖。你可以使用以下命令进行安装
npm install --save nestjs/oss ali-oss步骤 2: 配置 OSS 模块 在你的 Nest.js 项目中找到 app.module.ts 文件并在 imports 数组中引入 OssModule。同时在 providers 数组中配置 OSS_OPTIONS如下所示
import { Module } from nestjs/common;
import { OssModule, OSS_OPTIONS } from nestjs/oss;Module({imports: [OssModule.registerAsync({useFactory: () ({accessKeyId: your-access-key,accessKeySecret: your-access-secret,bucket: your-bucket-name,endpoint: your-oss-endpoint}),inject: [OSS_OPTIONS]})],providers: [{provide: OSS_OPTIONS,useFactory: () ({accessKeyId: your-access-key,accessKeySecret: your-access-secret,bucket: your-bucket-name,endpoint: your-oss-endpoint})}]
})
export class AppModule {}请将上述代码中的 your-access-key、your-access-secret、your-bucket-name 和 your-oss-endpoint 替换为实际的阿里云 OSS 配置。
步骤 3: 创建文件上传控制器 在你的 Nest.js 项目中创建一个文件上传的控制器。你可以使用 nestjs/oss 依赖提供的 UploadFile() 装饰器来处理上传的文件。
import { Controller, Post, UploadedFile, UseInterceptors } from nestjs/common;
import { OssService } from nestjs/oss;
import { FileInterceptor } from nestjs/platform-express;Controller(upload)
export class UploadController {constructor(private readonly ossService: OssService) {}Post()UseInterceptors(FileInterceptor(file))async uploadFile(UploadedFile() file) {const result await this.ossService.uploadFile(file.originalname, file.buffer);return result;}
}步骤 4: 创建路由 在你的 Nest.js 项目中创建一个路由将该路由绑定到文件上传的控制器上。
import { Module } from nestjs/common;
import { UploadController } from ./upload.controller;Module({controllers: [UploadController]
})
export class UploadModule {}步骤 5: 启动应用程序 最后你可以启动你的 Nest.js 应用程序并使用 POST 请求发送文件到 http://localhost:3000/upload 上传文件。
以上是一个基本的文件上传到阿里云 OSS 的功能实现的示例代码。请根据你的需求和项目配置进行相应的修改和调整。记得在实际项目中保护你的敏感信息如 accessKeyId 和 accessKeySecret。 非常抱歉我在之前的回答中犯了一个错误。实际上nestjs/oss 是一个不存在的模块我给出的代码示例是错误的。
要在 Nest.js 中实现文件上传到阿里云 OSS 的功能你可以按照以下步骤进行操作
步骤 1: 安装依赖 首先确保你已经在项目中安装了 ali-oss 这个依赖。你可以使用以下命令进行安装
npm install --save ali-oss步骤 2: 创建文件上传服务 在你的 Nest.js 项目中创建一个文件上传的服务。你可以创建一个名为 OssService 的服务类并使用 ali-oss 库来处理文件上传。
import { Injectable } from nestjs/common;
import OSS from ali-oss;Injectable()
export class OssService {private client: OSS;constructor() {this.client new OSS({accessKeyId: your-access-key,accessKeySecret: your-access-secret,bucket: your-bucket-name,endpoint: your-oss-endpoint});}async uploadFile(fileName: string, fileContent: Buffer): Promisestring {try {const result await this.client.put(fileName, fileContent);return result.url;} catch (error) {console.error(Failed to upload file:, error);throw error;}}
}请将上述代码中的 your-access-key、your-access-secret、your-bucket-name 和 your-oss-endpoint 替换为实际的阿里云 OSS 配置。
步骤 3: 创建文件上传控制器 在你的 Nest.js 项目中创建一个文件上传的控制器。你可以使用 Post() 装饰器绑定到上传文件的路由并在上传文件的方法中调用 OssService 中的 uploadFile 方法。
import { Controller, Post, UploadedFile, UseInterceptors } from nestjs/common;
import { FileInterceptor } from nestjs/platform-express;
import { OssService } from ./oss.service;Controller(upload)
export class UploadController {constructor(private readonly ossService: OssService) {}Post()UseInterceptors(FileInterceptor(file))async uploadFile(UploadedFile() file) {const result await this.ossService.uploadFile(file.originalname, file.buffer);return { url: result };}
}步骤 4: 创建路由 在你的 Nest.js 项目中创建一个路由将该路由绑定到文件上传的控制器上。
import { Module } from nestjs/common;
import { UploadController } from ./upload.controller;
import { OssService } from ./oss.service;Module({controllers: [UploadController],providers: [OssService]
})
export class UploadModule {}步骤 5: 启动应用程序 最后你可以启动你的 Nest.js 应用程序并使用 POST 请求发送文件到 http://localhost:3000/upload 上传文件。
以上是一个修正后的文件上传到阿里云 OSS 的功能实现的示例代码。请根据你的需求和项目配置进行相应的修改和调整。记得在实际项目中保护你的敏感信息如 accessKeyId 和 accessKeySecret。 Nest Js 项目实战
// TODO
参考
https://www.yuque.com/zhaocchen/fbyzbp/dh3cf5colgt3amvr