前言

  • 继续学习nest,前几天花了点时间重构了下dooring,学习计划拉的有点多。

中间件

  • 1、Middlewares中间件(作用在客户端和控制器之间)
  • 2、Guards守卫
  • 3、Interceptors拦截器(在流被操纵之前)
  • 4、Pipes管道
  • 5、Interceptors拦截器(在流被操纵之后)
  • 6、Exception filters过滤器(如果发现任何异常)

函数中间件

  • 函数中间件就是跟express中间件写法一样:
import { NextFunction, Request, Response } from 'express';
export const testMiddleWares = () => {
  return (_req: Request, _res: Response, next: NextFunction) => {
    console.log('test中间件');
    next();
  };
};
  • main.ts里调用:
app.use(testMiddleWares());
  • 然后发起请求后,就会走一下此中间件。其实这就是函数中间件,nest官网上全局中间件只写了这方法

Nest方式创建中间件

  • 使用命令:
nest g mi middlewares/log --no-spec
  • nest会创建出这样的:
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class LogMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log('nest中间件');
    next();
  }
}
  • 中间件还分好几种,一般配消费者,后面配控制器
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LogMiddleware).forRoutes(UserController);
  }
}
  • forRoutes这个还支持路由通配符:
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
forRoutes('cats');
forRoutes({ path: 'cats', method: RequestMethod.GET });
  • 多个中间件使用apply:
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);

守卫

nest g gu guard/auth
  • 生成这样的:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}
  • 从定义的返回值可以看见,支持同步或者异步的boolean,返回true就继续,否则就拦截了。
  • context定义是这样的:
export interface ExecutionContext extends ArgumentsHost {
    /**
     * Returns the *type* of the controller class which the current handler belongs to.
     */
    getClass<T = any>(): Type<T>;
    /**
     * Returns a reference to the handler (method) that will be invoked next in the
     * request pipeline.
     */
    getHandler(): Function;
}
  • arugmentsHost:
export interface ArgumentsHost {
    /**
     * Returns the array of arguments being passed to the handler.
     */
    getArgs<T extends Array<any> = any[]>(): T;
    /**
     * Returns a particular argument by index.
     * @param index index of argument to retrieve
     */
    getArgByIndex<T = any>(index: number): T;
    /**
     * Switch context to RPC.
     * @returns interface with methods to retrieve RPC arguments
     */
    switchToRpc(): RpcArgumentsHost;
    /**
     * Switch context to HTTP.
     * @returns interface with methods to retrieve HTTP arguments
     */
    switchToHttp(): HttpArgumentsHost;
    /**
     * Switch context to WebSockets.
     * @returns interface with methods to retrieve WebSockets arguments
     */
    switchToWs(): WsArgumentsHost;
    /**
     * Returns the current execution context type (string)
     */
    getType<TContext extends string = ContextType>(): TContext;
}
  • 大概意思是说这玩意是请求时获取可以拿到一些控制器方法之类的东西。
  • 使用方法有全局使用:
// 在main.ts中使用
app.useGlobalGuards(new AuthGuard())
  • 此时访问会发现每次必走守卫。
  • 如果守卫返回false,直接返回403
  • 模块注入:
providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
  • 如果走此模块的直接会过一遍守卫。
  • 控制器中使用:
@Controller('user')
@UseGuards(AuthGuard) // 在控制器层面控制
export class UserController {
  • 只要走此控制器就要走守卫,其他控制器不影响。这个比较好用。
  • 单个接口守卫:
@UseGuards(AuthGuard)
  @Get()
  async userList(): Promise<UserEntity[]> {
    return await this.userService.userList();
  }
  • 这个相当好用,非常灵活。

拦截器

  • https://docs.nestjs.com/interceptors
  • 拦截器是双向的,类似于洋葱模型的玩意。
  • 可以:
    在函数执行之前/之后绑定额外的逻辑
    转换从函数返回的结果
    转换从函数抛出的异常
    扩展基本函数行为
    根据所选条件完全重写函数 (例如, 缓存目的)
  • 创建拦截器:
nest g in interceptors/logging --no-spec
  • 会生成这样:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle();
  }
}
  • 几种使用方式
  • 在main.ts中引入:
app.useGlobalInterceptors(new LoggingInterceptor())
  • 模块中注入:
providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
  ],
  • 控制器中使用:
@UseInterceptors(new LoggingInterceptor())
  • 制作请求接口响应时间:
import {
  CallHandler,
  ExecutionContext,
  Injectable,
  Logger,
  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<any> {
    console.log('拦截器');
    const request = context.switchToHttp().getRequest();
    const method = request.method;
    const url = request.url;
    const now = Date.now();
    return next.handle().pipe(
      tap(() => {
        Logger.log(
          `${method} ${url} ${Date.now() - now}ms hhh`,
          context.getClass().name,
        );
      }),
    );
  }
}
  • 可以发现return后面就是响应,前面是请求过来。