• 【NestJS系列】核心概念:Middleware中间件


    前言

    用过expresskoa的同学,对中间件这个概念应该非常熟悉了,中间件可以拿到RequestResponse对象和next函数.

    一般来讲中间件有以下作用:

    • 执行任何代码
    • 对请求与响应拦截并改造
    • 结束request-response周期
    • 通过next()调用下一个中间件
    • 如果当前中间件没有结束当前request-response周期,必须调用next()函数,否则请求会处于挂起状态,阻塞整个应用

    中间件一般有两种:类中间件函数中间件

    类中间件

    创建类中间件

    使用@Injectable()装饰器,并且需要实现NestMiddleware接口(use方法)

    // Logger.middleware.ts
    import { Injectable, NestMiddleware } from "@nestjs/common";
    import { Request, Response } from "express";
    
    @Injectable()
    export class LoggerMiddleware implements NestMiddleware {
        use(req: Request, res: Response, next: () => void) {
            console.log('logger middleware', `url: ${req.url}`);
            next();
        }
    }
    

    使用类中间件

    类中间创建完之后,需要在模块中进行挂载,但@Module装饰器并没有中间件的相关配置,我们需要让module类实现NestModule接口,实现里面configure方法来进行挂载

    // user.module.ts
    import { Module, NestModule } from '@nestjs/common';
    import { UserService } from './user.service';
    import { UserController } from './user.controller';
    import { LoggerMiddleware } from '../middleware/Logger.middleware';
    @Module({
      controllers: [UserController],
      providers: [UserService]
    })
    export class UserModule implements NestModule {
      configure(consumer) {
        consumer
          .apply(LoggerMiddleware)
          .forRoutes(UserController);
      }
    }
    
    • apply方法表示挂载的是哪个中间件
    • forRoutes方法表示对哪个请求路径起作用,这种方式与app.use(path, middleware)作用是一样,只针对部分路径起作用
    • 当给forRoutes方法传递的是一个controller控制器时,那么该中间件则对整个控制器下的路径生效

    比如这里传递的是UserController控制器,那么针对该控制器下的路径都会生效

    • forRootes方法还能做更详细的配置,比如可以针对特定的请求方法、请求路径可以使用正则匹配(需要注意的是使用fastify驱动不能使用)
    export class UserModule implements NestModule {
      configure(consumer) {
        consumer
          .apply(LoggerMiddleware)
          .forRoutes({ path: 'user', method: RequestMethod.GET});
      }
    }
    
    • apply可以同时挂载多个中间件
    export class UserModule implements NestModule {
      configure(consumer) {
        consumer
          .apply(LoggerMiddleware, aaaMiddleware, ...)
          .forRoutes({ path: 'user', method: RequestMethod.GET});
      }
    }
    
    • forRoutes可以使用单个string路径,多个string路径,RouteInfo对象,单个Controller,多个Controller
    export class AppModule implements NestModule {
      configure(consumer) {
        consumer
          .apply(LoggerMiddleware, NjMiddleware, ...)
          .forRoutes(UserController, NjController, ...);
      }
    }
    
    • exclude可以用来排除不使用中间件的路径
    export class UserModule implements NestModule {
      configure(consumer) {
        consumer
          .apply(LoggerMiddleware)
          .exclude({ path: '/user/a', method: RequestMethod.GET})
          .forRoutes(UserController);
      }
    }
    

    需要注意的是forRoutes需要最后调用

    函数中间件

    这种方式较为简单,使用起来与类中间件一致

    创建函数中间件

    export function LoggerMiddleware(req: Request, res: Response, next: () => void) {
        console.log('logger middleware', `url: ${req.url}`);
        next();
    }
    

    使用函数中间件

    export class UserModule implements NestModule {
      configure(consumer) {
        consumer
          .apply(LoggerMiddleware)
          .exclude({ path: '/user/a', method: RequestMethod.GET})
          .forRoutes(UserController);
      }
    }
    

    全局中间件

    可以直接在入口文件main.ts中使用app.use来挂载中间件,这样挂载的中间件将全局生效

    app.use(LoggerMiddleware) // 日志中间件
    

    中间件其实可以用来实现很多功能,比如:日志系统、cors跨域处理、图片防盗等...

    对图片防盗感兴趣的可以看我这篇文章:你不知道的 HTTP Referer

  • 相关阅读:
    浅谈设计模式(六)
    Ubuntu20.04安装Beyond Compare 4.4.7
    实用新型专利和发明专利的区别是什么
    18.ROS编程:ROS中的时间c++
    高可用双机GPFS集群的的自动化部署脚本
    三勾商城(java+vue3)微信小程序商城+SAAS+前后端源码
    Linux下安装Mysql
    Trie树的实现(代码实现) [Java]
    Netty 的整体架构是怎样的?
    开源自定义表单设计器的优点体现在哪些方面?
  • 原文地址:https://www.cnblogs.com/songyao666/p/17663268.html