建设网站怎么建设分类,网站建设公司网站建设专业品牌,长沙县网页设计培训,营销推广的主要方式拦截器会用到RxJs#xff0c;所以在学习拦截器之前可以先了解一下它。
拦截器是使用Injectable()装饰器装饰的类并且实现了接口NestInterceptor。
拦截器受到 AOP(面向切面编程)技术的启发#xff0c;具有如下的功能#xff1a;
在方法执行之前/之后绑定额外的逻辑转换函…拦截器会用到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): Observableany {console.log(Before...);const now Date.now();return next.handle().pipe(tap(() console.log(After... ${Date.now() - now}ms)));}
}说明NestInterceptorT, 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 ResponseT {data: T;
}Injectable()
export class TransformInterceptorTimplements NestInterceptorT, ResponseT
{intercept(context: ExecutionContext,next: CallHandler): ObservableResponseT {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): Observableany {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): Observableany {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): Observableany {const isCached true;if (isCached) {return of([]);}return next.handle();}
}在上述的例子中CacheInterceptor里面有一个硬编码常量isCached和一个硬编码响应数组。需要注意的关键点是我们在这里返回一个由 RxJS 运算符创建的新流因此根本不会调用 of()路由处理程序。当有人调用使用 的端点时将立即返回响应硬编码的空数组。为了创建通用解决方案您可以利用并创建自定义装饰器。后续的笔记中会举出一些更详细的例子。