RxJS 是一个组织异步逻辑的库,可简化异步逻辑和回调的编写
Nest 的 interceptor 集成了 RxJS,可以用它来处理响应
新建项目看看
nest new interceptor-rxjs -p npm
新建
nest g interceptor aaa --flat --no-spec
记录接口时间
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';
@Injectable()
export class AaaInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`Using... ${Date.now() - now}ms`)),
);
}
}
使用
这是 interceptor 最基本使用
使用下 RxJS operator
map
nest g interceptor map-test --flat --no-spec
使用 map operator 来对 controller 返回的数据做一些修改
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { map, Observable } from 'rxjs';
@Injectable()
export class MapTestInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(map(data => {
return {
code: 200,
message: 'success',
data
}
}))
}
}
controller 中使用
tap
nest g interceptor tap-test --flat --no-spec
使用 tap operator 来添加一些日志、缓存等逻辑
import { AppService } from './app.service';
import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';
@Injectable()
export class TapTestInterceptor implements NestInterceptor {
constructor(private appService: AppService) {}
private readonly logger = new Logger(TapTestInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(tap((data) => {
// 这里是更新缓存的操作,这里模拟下
this.appService.getHello();
this.logger.log(`log log log`, data);
}))
}
}
使用
catchError
controller 里很可能会抛出错误,这些错误会被 exception filter 处理,返回不同的响应,在那之前,可以在 interceptor 先处理下
nest g interceptor catch-error-test --flat --no-spec
更新下
import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { catchError, Observable, throwError } from 'rxjs';
@Injectable()
export class CatchErrorTestInterceptor implements NestInterceptor {
private readonly logger = new Logger(CatchErrorTestInterceptor.name)
intercept (context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(catchError(err => {
this.logger.error(err.message, err.stack)
return throwError(() => err)
}))
}
}
使用
错误打印
还有一次错误打印
一次是在 interceptor 里打印的,一次是 exception filter 打印
timeout
接口如果长时间没返回,要给用户一个接口超时的响应,可以用 timeout operator
nest g interceptor timeout --flat --no-spec
更新
import { CallHandler, ExecutionContext, Injectable, NestInterceptor, RequestTimeoutException } from '@nestjs/common';
import { catchError, Observable, throwError, timeout, TimeoutError } from 'rxjs';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
timeout(3000),
catchError(err => {
if(err instanceof TimeoutError) {
console.log(err);
return throwError(() => new RequestTimeoutException());
}
return throwError(() => err);
})
)
}
}
会在 3s 没收到消息的时候抛一个 TimeoutError。
然后用 catchError 处理,如果是 TimeoutError,就返回 RequestTimeoutException,这个有内置的 exception filter 会处理成对应的响应格式。
其余错误就直接 throw Error 抛出去
使用
此处处理
可以换一个试试
再试试 全局的 interceptor
这种是手动 new 的,没法注入依赖
但很多情况下我们是需要全局 interceptor 的,而且还用到一些 provider,怎么办呢?
nest 提供了一个 token,用这个 token 在 AppModule 里声明的 interceptor,Nest 会把它作为全局 interceptor
在这个 interceptor 里注入了 appService
可以看到全局 interceptor 生效了,而且这个 hello world 就是注入的 appService 返回的