Nest.js - 拦截器

概念

拦截器使用@Injectable装饰器,并实现NestInterceptor接口。

  • 在函数执行之前/之后绑定额外的逻辑
  • 转换从函数返回的结果
  • 转换从函数抛出的异常
  • 扩展基本函数行为
  • 根据所选条件完全重写函数(如缓存目的)
自定义拦截器
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 {
    console.log('before');

    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`));
      );
  }
}
  • 每个拦截器都有intercept(ExecutionContext, CallHandler)方法,第一个参数是执行上下文,第二个参数是调用处理程序
  • CallHandler是一个包装执行流的对象。因此。必须手动调用hander()方法,最终方法才会被触发;
  • 在函数执行前后添加额外的逻辑。在需要记录与应用程序的交互时很有用。如存储用户调用异步调度事件计算时间戳
    +handle()返回一个Observable,可使用tap()等运算符,在可观察序列的正常/异常终止时调用函数
绑定拦截器
//@UseInterceptors(new LoggingInterceptor())
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
  • 拦截器的范围有: 方法层面,控制器层面,全局层面
响应映射

handle()返回一个Observable,该流包含了从路由处理程序返回的值,可使用map()运算符改变它。

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export interface Response {
  data: T;
}

@Injectable()
export class TransformInterceptor implements NestInterceptor> {
  intercept(context: ExecutionContext, next: CallHandler): Observable> {
    return next.handle().pipe(map(data => ({ data })));
  }
}
  • 创建的TransformInterceptor将打包响应并将其分配给data属性
  • 假如处理程序返回空数组[],请求将被改写成:{ data: [] }

例如需要在全局将每个发生的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 {
    return next
      .handle()
      .pipe(map(value => value === null ? '' : value ));
  }
}
// 绑定拦截器
const app = await NestFactory.create(ApplicationModule);
app.useGlobalInterceptors(new ExcludeNullInterceptor ());
异常映射

利用catchError()操作符来覆盖抛出的异常

// exception.interceptor.ts
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 {
    return next
      .handle()
      .pipe(
        catchError(err => throwError(new BadGatewayException())),
      );
  }
}
Stream重写

假如由于性能问题,需要从缓存中获取。如缓存拦截器,希望完全阻止调用处理程序,并返回不同的值。

// cache.interceptor.ts
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 {
    const isCached = true;
    if (isCached) {
      return of([]);
    }
    return next.handle();
  }
}
  • 此处有硬编码的isCached变量和响应[],通过of()运算符创建并返回一个新的流,因此路由处理程序根本不会被调用;
  • 为了更好地通用性,可以利用Reflector反射器创建自定义修饰符。
更多操作符

Rxjs运算符操作流提供了很多功能。

// timeout.interceptor.ts

import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from '@nestjs/common';
import { Observable, throwError, TimeoutError } from 'rxjs';
import { catchError, timeout } from 'rxjs/operators';

@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable {
    return next.handle().pipe(
      timeout(5000),
      catchError(err => {
        if (err instanceof TimeoutError) {
          return throwError(new RequestTimeoutException());
        }
        return throwError(err);
      }),
    );
  };
};

  • 5秒后,请求处理将被取消。您还可以在抛出之前添加自定义逻辑RequestTimeoutException(例如,释放资源)。

你可能感兴趣的:(Nest.js - 拦截器)