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()路由处理程序。当有人调用使用 的端点时,将立即返回响应(硬编码的空数组)。为了创建通用解决方案,您可以利用并创建自定义装饰器。后续的笔记中会举出一些更详细的例子。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/18080.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

express学习笔记2 - 三大件概念

中间件 中间件是一个函数&#xff0c;在请求和响应周期中被顺序调用&#xff08;WARNING&#xff1a;提示&#xff1a;中间件需要在响应结束前被调用&#xff09; 路由 应用如何响应请求的一种规则 响应 / 路径的 get 请求&#xff1a; app.get(/, function(req, res) {res…

以beam search为例,详解transformers中generate方法(上)

以beam search为例&#xff0c;详解transformers中generate方法&#xff08;上&#xff09; 1. generate的代码位置2. GenerationMixin概览3. generate签名4. generate过程4.1 读取并更新generation config4.2 补充没有传入的参数4.3 定义模型输入4.4 定义模型的其他参数4.5 对…

Mysql存储过程基本语法

目录 存储过程MYSQL基础语法游标&#xff08;作用范围存储过程&#xff09;事务&#xff08;Demo&#xff09;其他操作Demo 存储过程 MYSQL 基础语法 #创建存储过程 #参数格式 #in输入参数 out输出参数 inout既可以输入也可以输出 create or replace procedure 存储过程名…

react常用知识点

React是一个用于构建用户界面的JavaScript库。以下是React常用的知识点&#xff1a; 组件&#xff1a;React将用户界面分解成小而独立的组件&#xff0c;每个组件都有自己的状态和属性&#xff0c;并且可以通过组合这些组件来构建复杂的用户界面。 // 函数组件示例 function We…

torch.load 报错 ModuleNotFoundError 或 AttributeError

Python 3.11.3 (main, Apr 7 2023, 19:25:52) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin Type "help", "copyright", "credits" or "license" for more information.正常情况下&#xff0c;我们会使用 torch.save 保存模型的 …

向 Maven 中央仓库上传一个修改过的基于jeecg的autoPOI的 jar包记录

1、注册https://issues.sonatype.org/账号 下面就代表注册好了&#xff0c;同时提交的工单也通过了 2、这里主要是goupId 需要进行认证&#xff0c;需要到域名注册商近一个txt的解析&#xff0c;以便确保这个是你的 通过下面来验证你的域名信息&#xff0c;这里主要是上面的工…

git命令分类合集

配置 git config --global user.name <name>&#xff1a;设置全局用户名 git config --global user.email <email>&#xff1a;设置全局用户邮箱 git config --global core.editor <editor>&#xff1a;设置全局文本编辑器创建与克隆仓库 git init&#xf…

面试题:说一说深拷贝和浅拷贝?

JavaScript中存在两大数据类型&#xff1a; 基本类型 和 引用类型 基本类型数据保存在在栈内存中 引用类型数据保存在堆内存中&#xff0c;引用数据类型的变量是一个指向堆内存中实际对象的引用&#xff0c;存在栈中 深拷贝和浅拷贝都只针对于引用类型。 一、 浅拷贝&#xff1…

【2023年11月第四版教材】《第1章-信息化发展之<3 现代化创新发展>》

第1章-信息化发展之&#xff1c;3 现代化创新发展&#xff1e; 3 现代化创新发展3.1 农业现代化3.2 两化融合&#xff08;17下2&#xff09;&#xff08;18下2&#xff09; &#xff08;22下12)3.3 智能制造3.4 消费互联网 3 现代化创新发展 3.1 农业现代化 要素具体内容农业…

Cpp7 — 继承和多态

继承 -------- 面向对象的三大特性之一 面向对象的三大特性&#xff1a;封装、继承、多态 封装&#xff1a;把数据和方法都封装在一起&#xff0c;想给你访问的变成共有&#xff0c;不想给访问的&#xff0c;写成私有。 继承&#xff1a;继承是类设计层次的复用 多态&#…

[SQL挖掘机] - 索引

介绍: 当你在数据库中进行查询时&#xff0c;索引是一种用于提高查询性能的重要工具。索引是对表中的一列或多列进行排序的数据结构&#xff0c;它可以快速定位到满足特定条件的记录&#xff0c;从而减少了查询所需的时间和资源。 在数据库中使用索引的主要好处包括&#xff…

【AGI】Copilot AI编程辅助工具安装教程

1. 基础激活教程 GitHub和OpenAI联合为程序员们送上了编程神器——GitHub Copilot。 但是&#xff0c;Copilot目前不提供公开使用&#xff0c;需要注册账号通过审核&#xff0c;我也提交了申请&#xff1a;这里第一期记录下&#xff0c;开启教程&#xff0c;欢迎大佬们来讨论…

通向架构师的道路之apache性能调优

一、总结前一天的学习 在前两天的学习中我们知道、了解并掌握了Web Server结合App Server实现单向Https的这样的一个架构。这个架构是一个非常基础的J2ee工程上线布署时的一种架构。在前两天的教程中&#xff0c;还讲述了Http服务 器、App Server的最基本安全配置&#xff08;…

java 数组的使用

数组 基本介绍 数组可以存放多个同一类型的数据&#xff0c;数组也是一种数据类型&#xff0c;是引用类型。 即&#xff1a;数组就是一组数据。 数组的使用 1、数组的定义 方法一 -> 单独声明 数据类型[] 数组名 new 数据类型[大小] 说明&#xff1a;int[] a new int…

C/C++算法——散列表

1、散列表介绍 散列表的英文叫Hash Table&#xff0c;我们平时也叫它哈希表或者Hash 表。散列表用的是数组支持按照下标随机访问数据的特性&#xff0c;所以散列表其实就是数组的一种扩展&#xff0c;由数组演化而来。可以说&#xff0c;如果没有数组&#xff0c;就没有散列表。…

【SpringBoot】85、SpringBoot中Boolean类型数据转0/1返回序列化配置

在 SpringBoot 中,前端传参数 0,1,后端可自动解析为 boolean 类型,但后端返回前端 boolean 类型时,却无法自动转换为 0,1,所以我们需要自定义序列化配置,将 boolean 类型转化为 0,1 1、类型对应 boolean 类型有false,true对应的 int 类型0,12、序列化配置 import com.f…

iOS——锁与死锁问题

iOS中的锁 什么是锁锁的分类互斥锁1. synchronized2. NSLock3. pthread 递归锁1. NSRecursiveLock2. pthread 信号量Semaphore1. dispatch_semaphore_t2. pthread 条件锁1. NSCodition2. NSCoditionLock3. POSIX Conditions 分布式锁NSDistributedLock 读写锁1. dispatch_barri…

MySQL优化

目录 一. 优化 SQL 查询语句 1.1. 分析慢查询日志 1.2. 优化 SQL 查询语句的性能 1.2.1 优化查询中的索引 1.2.2 减少表的连接&#xff08;join&#xff09; 1.2.3 优化查询语句中的过滤条件 1.2.4 避免使用SELECT * 1.2.5 优化存储过程和函数 1.2.6 使用缓存 二. 优化表结构…

超全整理,Jmeter性能测试-常用Jmeter第三方插件详解(超细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Jmeter作为一个开…

React(4)

1.属性&#xff08;props&#xff09;初始 状态state都是组件内部写的&#xff0c;也就是A组件内的state就只能A组件里面用&#xff0c;其他组件复用不了。因此属性props就可以。 比如一个导航栏&#xff0c;首页有&#xff0c;购物车有&#xff0c;我的有&#xff0c;他们三个…