NestJS 的 拦截器 学习
拦截器会用到RxJs,所以在学习拦截器之前可以先了解一下它。
拦截器是使用@Injectable()装饰器装饰的类并且实现了接口NestInterceptor。
拦截器受到 AOP(面向切面编程)技术的启发,具有如下的功能:
- 在方法执行之前/之后绑定额外的逻辑
- 转换函数返回的结果
- 转换函数抛出的异常
- 扩展基本功能行为
- 根据特定条件完全覆盖函数
基础知识
每个拦截器都实现 Intercept() 方法,该方法采用两个参数。第一个参数是ExecutionContext的实例,ExecutionContext继承ArgumentsHost。另外一个参数则是CallHandler,在后面的小节中会介绍。
ExecutionContext
ExecutionContext对象在守卫学习笔记已经做过简要的介绍,我们会在后续的笔记中进行详细说明。
CallHandler 接口说明
CallHandler 接口需要实现两个方法,一个是handle()方法,这个方法可以在拦截器中某个时刻调用路由处理程序的方法。另外一个方法是intercept()方法,这个方法可以对请求/响应流进行包装,因此可以在最终路由处理程序之前和之后实现自定义的逻辑。两个方法的关系是如果在 intercept() 方法的实现中不调用 handle()方法,则根本不会执行路由处理程序方法。
所以很明显,您可以在调用 handle()之前执行的 intercept()方法中编写代码,但是如何影响之后发生的事情呢?因为 handle()方法返回一个 Observable,所以我们可以使用强大的 RxJS 运算符来进一步操作响应。使用面向方面的编程术语,路由处理程序的调用(即调用 handle())称为切入点,表明这是插入我们的附加逻辑的点。
上面我们做了一些概述,我们现在结合例子来说明一下。在提供者笔记中我们有一个 POST /commodity/save的请求接口,这个请求路径是在CommodityController里面进行声明的。假如我们在这个请求路径上绑定拦截器的话,如果在此过程中的任何地方调用了不调用 handle()方法的拦截器,则 create()方法将不会被执行。一旦 handle()被调用(并且它的 Observable 已经返回),create()处理程序将被触发。一旦通过 Observable 接收到响应流,就可以对流执行附加操作,并将最终结果返回给调用者。
切面拦截
通过上述的介绍,我们来看看第一个用例,这个用例主要是使用拦截器来记录用户交互(存储用户调用、异步分派事件或计算时间戳)。具体实例代码如下:
import {Injectable,NestInterceptor,ExecutionContext,CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";@Injectable()
export class LoggingInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {console.log("Before...");const now = Date.now();return next.handle().pipe(tap(() => console.log(`After... ${Date.now() - now}ms`)));}
}
说明:NestInterceptor<T, R>是一个通用的接口,其中 T 指示 an 的类型 Observable(支持响应流),R 是 所包装的值的类型 Observable。
由于 handle()返回 RxJS Observable,我们有多种操作符可供选择来操作流。在上面的示例中,我们使用了 tap()运算符,它在可观察流正常或异常终止时调用我们的匿名日志记录函数,但不会以其他方式干扰响应周期。
绑定拦截器
我们编写好拦截器后,可以使用@UseInterceptors()来绑定拦截器。与管道和守卫一样,拦截器可以是控制器范围、方法范围或全局范围。
@Controller("commodity")
@UseInterceptors(LoggingInterceptor)
export class CommodityController {}
上述的例子中,我们的CommodityController所有路由处理程序都将被LoggingInterceptor进行拦截,当我们请求任何接口后,都可以在控制台看到如下的信息:
Before...
After... 2ms
请注意,我们传递了 LoggingInterceptor 类型(而不是实例),将实例化的责任留给框架并启用依赖项注入。与管道、防护和异常过滤器一样,我们也可以传递就地实例:
@Controller("commodity")
@UseInterceptors(new LoggingInterceptor())
export class CommodityController {}
全局拦截器设置可以如下:
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
全局范围拦截器会应用于整个应用程序、每个控制器和每个路由。当我们需要在某个模块注入全局范围拦截器的依赖项是不行的,因为全局范围拦截器是在没有上下文环境下完成,所以没有上下文的支持,模块是不能找到对应实例。为了解决这个问题,我们可以用下面的方式来进行拦截器的注入:
import { Module } from "@nestjs/common";
import { APP_INTERCEPTOR } from "@nestjs/core";@Module({providers: [{provide: APP_INTERCEPTOR,useClass: LoggingInterceptor,},],
})
export class AppModule {}
响应映射
我们已经知道 handle()返回一个 Observable. 该流包含从路由处理程序返回的值,因此我们可以使用 RxJS 的 map()运算符轻松更改它。
让我们创建 TransformInterceptor,它将以一种简单的方式修改每个响应来演示该过程。它将使用 RxJS 的 map()运算符将响应对象分配给 data 新创建的对象的属性,并将新对象返回给客户端。具体实例如下:
import {Injectable,NestInterceptor,ExecutionContext,CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";export interface Response<T> {data: T;
}@Injectable()
export class TransformInterceptor<T>implements NestInterceptor<T, Response<T>>
{intercept(context: ExecutionContext,next: CallHandler): Observable<Response<T>> {return next.handle().pipe(map((data) => ({ data })));}
}
// 路由调用的方法
findAll(): Commodity[] {return this.commoditys;
}
调用上述/commodity/all请求地址后,我们可以看到如下的返回结果:
{"data": [{"id": 1,"username": "123","password": "123"},{"id": 2,"username": "456","password": "456"}]
}
说明:嵌套拦截器可以使用同步和异步 intercept()方法。async 如果需要, 您可以简单地切换该方法。
我们再举一个全局拦截器的例子,假如我们的请求参数可能会有带有 null 的参数,而 null 在程序处理时可能会出现异常,为此我们需要把 null 转为空字符串,这样我们一个一个方法去处理的话不太友好,这样我们就可以使用全局拦截器来处理,具体的例子如下:
import {Injectable,NestInterceptor,ExecutionContext,CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";@Injectable()
export class ExcludeNullInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {return next.handle().pipe(map((value) => (value === null ? "" : value)));}
}
异常拦截器例子
import {Injectable,NestInterceptor,ExecutionContext,BadGatewayException,CallHandler,
} from "@nestjs/common";
import { Observable, throwError } from "rxjs";
import { catchError } from "rxjs/operators";@Injectable()
export class ErrorsInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {return next.handle().pipe(catchError((err) => throwError(() => new BadGatewayException())));}
}
流覆盖
有几个原因导致我们有时可能希望完全阻止调用处理程序并返回不同的值。一个明显的例子是实现缓存以提高响应时间。让我们看一下一个简单的缓存拦截器,它从缓存返回其响应。在实际示例中,我们需要考虑其他因素,例如 TTL、缓存失效、缓存大小等。在这里,我们将提供一个演示主要概念的基本示例。
import {Injectable,NestInterceptor,ExecutionContext,CallHandler,
} from "@nestjs/common";
import { Observable, of } from "rxjs";@Injectable()
export class CacheInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {const isCached = true;if (isCached) {return of([]);}return next.handle();}
}
在上述的例子中,CacheInterceptor里面有一个硬编码常量isCached和一个硬编码响应数组。需要注意的关键点是,我们在这里返回一个由 RxJS 运算符创建的新流,因此根本不会调用 of()路由处理程序。当有人调用使用 的端点时,将立即返回响应(硬编码的空数组)。为了创建通用解决方案,您可以利用并创建自定义装饰器。后续的笔记中会举出一些更详细的例子。
相关文章:
NestJS 的 拦截器 学习
拦截器会用到RxJs,所以在学习拦截器之前可以先了解一下它。 拦截器是使用Injectable()装饰器装饰的类并且实现了接口NestInterceptor。 拦截器受到 AOP(面向切面编程)技术的启发,具有如下的功能: 在方法执行之前/之后绑定额外的逻辑转换函…...
Spring AOP 中的代理对象是怎么创建出来的?
文章目录 1. AOP 用法2. 原理分析2.1 doCreateBean2.2 postProcessAfterInitialization2.3 getAdvicesAndAdvisorsForBean2.3.1 findCandidateAdvisors2.3.2 findAdvisorsThatCanApply2.3.3 extendAdvisors 2.4 createProxy 今天和小伙伴们聊一聊 Spring AOP 中的代理对象是怎么…...
解决@Scope(“prototype“)不生效的问题
目录 Scope(“prototype“)不生效Scope(“prototype“)正确用法——解决Bean多例问题 1.问题,Spring管理的某个Bean需要使用多例2.问题升级3. Spring给出的解决问题的办法(解决Bean链中某个Bean需要多例的问题) Scope(“prototype“)不生效 …...
Mybatis 知识点
Mybatis 知识点 1.1 Mybatis 简介 1.1.1 什么是 Mybatis Mybatis 是一款优秀的持久层框架支持定制化 SQL、存储过程及高级映射Mybatis 几乎避免了所有的 JDBC 代码和手动设置参数以及获取结果集MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO…...
PHP中关于is,between,in等运算符的用法是什么?
我们学习了解了这么多关于PHP的知识,不知道你们对PHP中关于is,between,in等运算符的用法是什么?是否已经完全掌握了呢,如果没有,那就跟随本篇文章一起继续学习吧 相关推荐:关于PHP中的增删改如…...
2023-07-29:华清远见嵌入式2017年线下班:文件IO笔记
这里写目录标题 华清远见嵌入式2017年线下班:文件IO笔记文件权限文件IO文件创建和打开操作文件关闭操作出错处理创建设备文件 || create || 老师自己忘了文件读操作练习:计算文件的大小?文件写操作练习:打开file1和file2ÿ…...
2023年第四届“华数杯”数学建模思路 - 复盘:光照强度计算的优化模型
文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米,宽为12米࿰…...
Typescript第七章 处理错误(返回null,抛出异常,返回异常,Option类型)
第七章 处理错误 Typescript竭尽所能,把运行时异常转移到编译时。Typescript是功能丰富的系统,加上强大的静态和符号分析能力,包揽了大量辛苦的工作。 但是有些问题是无法避免的,比如网络和文件系统异常,解析用户输入…...
Qt库xcb问题
首先在~/.bashrc中加入 export QT_DEBUG_PLUGINS1然后看具体的报错 查看某个库链接的库: ldd libqxcb.so然后找到真正缺少的库,再在路径下搜索,然后建立软链接。 https://blog.csdn.net/LOVEmy134611/article/details/107212845 https://…...
C++ | 哈希表的实现与unordered_set/unordered_map的封装
目录 前言 一、哈希 1、哈希的概念 2、哈希函数 (1)直接定址法 (2)除留余数法 (3)平方取中法(了解) (4)随机数法(了解) 3、哈…...
【漏洞挖掘】Xray+rad自动化批量漏洞挖掘
文章目录 前言一、挖掘方法二、使用步骤工具安装使用方法开始挖掘 总结 前言 自动化漏洞挖掘是指利用计算机程序和工具来扫描、分析和检测应用程序、网络和系统中的安全漏洞的过程。这种方法可以帮助安全专家和研究人员更高效地发现和修复潜在的安全威胁,从而提高整…...
Swagger UI教程 API 文档和Node的使用
在团队开发中,一个好的 API 文档可以减少很多交流成本,也可以使一个新人快速上手业务。 前言 swagger ui是一个API在线文档生成和测试的利器,目前发现最好用的。为什么好用?Demo 传送门 支持API自动生成同步的在线文档 这些文档可…...
P5691 [NOI2001] 方程的解数
[NOI2001] 方程的解数 题目描述 已知一个 n n n 元高次方程: ∑ i 1 n k i x i p i 0 \sum\limits_{i1}^n k_ix_i^{p_i} 0 i1∑nkixipi0 其中: x 1 , x 2 , … , x n x_1, x_2, \dots ,x_n x1,x2,…,xn 是未知数, k 1 ,…...
rust里用什么表示字节类型?
在Rust中,字节可以使用 u8 类型来表示。 u8 是一个无符号8位整数类型,可以表示0到255之间的值,对应于一个字节的范围。 以下是一个示例,演示了如何声明和使用字节: fn main() {let byte: u8 65; // 表示字母A的ASCI…...
CMake简介
文章目录 为什么需要头文件为什么 C 需要声明头文件 - 批量插入几行代码的硬核方式头文件进阶 - 递归地使用头文件 CMake什么是编译器多文件编译与链接CMake 的命令行调用为什么需要库(library)CMake 中的静态库与动态库CMake 中的子模块子模块的头文件如…...
[threejs]相机与坐标
搞清相机和坐标的关系在threejs初期很重要,否则有可能会出现写了代码,运行时一片漆黑的现象,这种情况就有可能是因为你相机没弄对。 先来看一下threejs中的坐标(世界坐标) 坐标轴好理解,大家只需要知道在three中不同颜色代表的轴…...
Qt信号与槽机制的基石-MOC详解
引入 上篇讲到了信号与槽就是实现的观察者模式,那具体如何生成映射表就是moc做的事情。 一、moc简介 1. moc的定义 moc 全称是 Meta-Object Compiler,也就是“元对象编译器”,它主要用于处理C源文件中的非标准C代码。Qt 程序在交由标准编…...
关于单体架构缓存刷新实现方案
背景 如果各位看官是分布式项目应该都采用分布式缓存了,例如redis等,分布式缓存不在本次讨论范围哈;我个人建议是,如果是用户量比较大,建议采用分布式缓存机制,后期可以很容易前后到分布式服务或微服务。 …...
洞悉安全现状,建设网络安全防护新体系
一、“网络攻防演练行动“介绍 国家在2016年发布《网络安全法》,出台网络安全攻防演练相关规定:关键信息基础设施的运营者应“制定网络安全事件应急预案,并定期进行演练”。同年“实战化网络攻防演练行动”成为惯例。由公安部牵头࿰…...
spring中怎么通过静态工厂和动态工厂获取对象以及怎么通过 FactoryBean 获取对象
😀前言 本章是spring基于XML 配置bean系类中第4篇讲解spring中怎么通过静态工厂和动态工厂获取对象以及怎么通过 FactoryBean 获取对象 🏠个人主页:尘觉主页 🧑个人简介:大家好,我是尘觉,希望…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
