NestJS06-Middleware

440 阅读4分钟

中间件middleware是一个函数,它在路由处理程序之前被调用。中间件函数可以访问请求和响应对象,以及应用程序请求响应周期中的next()中间件函数。下一个中间件函数通常由一个名为next的变量表示。

中间件.png

Nest的中间件默认的情况下是和express相同的。

  • 中间件功能可以执行以下任务:
    • 执行任何代码
    • 对请求和响应对象进行更改
    • 结束请求响应周期
    • 调用堆栈中的下一个中间件函数
    • 如果当前的中间件函数没有结束请求响应周期,它必须调用next()将控制权传递给下一个中间件函数。否则,请求将被搁置

实现自定义Nest中间件可以是函数还可以是用@Injectable()装饰的类,类必须实现NestMiddleware接口,而函数没有任何特殊要求。

  • 生成中间件
# 生成中间件
nest g mi logger --no-spec
  • 中间件代码
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}

依赖注入

Nest中间件完全支持依赖注入。与提供程序和控制器一样,它们能够注入同一模块中可用的依赖项。通常是通过构造函数完成的。

应用中间件

中间件不能写在@Module()装饰器里面,我们可以通过configure()方法来设定。Modules则需要实现NestModule接口。

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { LoggerMiddleware } from './logger/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
  controllers: [],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('cats')
  }
}

上面的的例子我们是在路由处理上加了,中间件,也可以传入一个对象,这个对象包含pathmethod2个项目。这个指定对应的请求方式。

@Module({
  imports: [CatsModule],
  controllers: [],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'cats', method: RequestMethod.GET })
  }
}
提示:可以使用async/await使configure()方法异步(例如,可以在configure(方法体中等待异步操作的完成)。

路由通配符

也支持基于模式的路由。例如,星号用作通配符,并将匹配任何字符组合:

    // 路由中间件-路由通配符
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'ab*cd', method: RequestMethod.ALL })

ab*cd路由路径将匹配abcdab_cdabecd等。字符+*、,和()可以在路由路径中使用,并且是它们的正则表达式对应的子集。连字符(-)和点(.)由基于字符串的路径字面解释。

警告:fastify包使用最新版本的regexp包路径,该包不再支持通配符星号*。相反,必须使用参数(例如,(.*)、:splat*)。

中间件消费者

MiddlewareConsumer是一个帮助类,它提供了几种管理中间件的内置方法发。所有这些都可以链式调用。forRoutes()方法可以接受单个字符串、多个字符串、RouteInfo对象、控制器类甚至多个控制器类。在大多数情况下,您可能只需要传递一个用逗号分隔的控制器列表。以下是单个控制器的示例:

    // 路由中间件-中间件消费者
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(CatsController)
说明:apply()方法可以使用单个中间件,也可以使用多个参数来指定多个中间件。

路由排除

有些时候我们可能像排除一些特定的路由,可以使用exclude()这个方法。此方法可以采用单个字符串、多个字符串或标识要排除的路由的RouteInfo对象,如下所示:

consumer  
  .apply(LoggerMiddleware)  
  .exclude(  
    { path: 'cats', method: RequestMethod.GET },  
    { path: 'cats', method: RequestMethod.POST },  
    'cats/(.*)',  
  )  
  .forRoutes(CatsController);
exclude参数里面也支持正则表达式,需要利用:https://github.com/pillarjs/path-to-regexp#parameters

函数中间件

LoggerMiddleware写的比较简单。没有成员,没有附加的方法,没有依赖项。所以我们也可以用一个函数来替代他。

// logger.middleware.ts
export function logger(req: Request, res: Response, next: NextFunction) {
  console.log('func Request...')
  next()
}

// app.module.ts    
// 中间件-函数
consumer
  .apply(logger)
  .forRoutes(CatsController);
提示:在您的中间件不需要任何依赖项时,请考虑使用更简单的功能中间件替代方案。

多个中间件

如上所述,为了绑定顺序执行的多个中间件,只需在apply()方法中提供逗号分隔的列表:

consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);

全局中间件

如果我们想一次将中间件绑定到每个注册的路由,我们可以使用INestApplication实例提供的use()方法:

const app = await NestFactory.create(AppModule);  
app.use(logger);  
await app.listen(3000);

本章代码

代码