D5 的训练还是跟 Nest.js 的核心部件相关,主要介绍了拦截器,自定义路由参数装饰器,还要了解一下自定义管道,这是核心部件相关训练的最后一天。
后面我们要去创建一个全新的 Nest.js 项目,主要去了解怎么把数据放到数据库里,怎么让应用帮我们把需要的数据提取出来,如何验证用户身份,还有怎么上传文件等等。
后面的训练任务会大量重复之前训练试验过的东西,所以如果之前没有跟上的同学,可以直接从 D6 开始训练。
拦截器
拦截器(Interceptors),如果你想在客户端请求到达请求处理器之前,或者请求处理器响应回数据之后,去做一些事情,就可以通过拦截器这种东西去实现。
先看一下简单的拦截器:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { const now = Date.now(); console.log('before...'); console.log(`I'm a interceptor.`); return next .handle() .pipe( tap(() => console.log(`after... ${Date.now() - now}ms`)) ); } }
上面这个拦截器演示了怎么在请求处理器被调用之前与之后去做一些事情。定义拦截器的这个类得去实施一下 NestInterceptor 接口 ,所以类里面要添加一个需要的 intercept 方法。
这个 intercept 方法有两个参数,context 还有 next。通过 context 参数我们可以得到请求与响应,next 参数是个 CallHandler,我们在 intercept 方法里,必须要执行 next 上的 handle() 方法,才能执行对应的请求处理器。也就是如果不在拦截器里调用这个方法,请求就不会接触到请求处理器。
handle 很厉害,它能返回 Observable,可以把它想成是一条流水线,在这条流水线上我们可以安插各种功能的操作员去处理流水线上的东西。给 handle 接收一个管道(pipe),在这个管道里可以添加 rxjs 里面的各种操作员去做一些事情。
注意上面提到的管道并不是 Nest.js 里的管道,上面的管道是 rxjs 里的管道,管道这个词儿经常会在程序开发里出现,要留意它真正表示的东西是什么。
下面这个拦截器修改了异常处理:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor, BadGatewayException } 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(error => throwError(new BadGatewayException())) ); } }
上面这个拦截器的作用是拦截应用里响应的异常情况,这个拦截器捕捉到应用的异常情况,会转换这个异常,把它变成另外一种异常情况。
下面这个拦截器转换了响应的数据:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export interface Response { data: T; } @Injectable() export class TransformInterceptor implements NestInterceptor<T, Response> { intercept(context: ExecutionContext, next: CallHandler): Observable<Response> { return next .handle() .pipe( map(data => ({ data })) ); } }
上面这个拦截器修改了响应的数据格式,它会把响应的数据放在一个 data 属性里面。