首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

中间件

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

默认情况下,Nest中间件等同于Express中间件。以下是从官方Express文档中复制的中间件功能的绝佳列表:

中间件功能可以执行以下任务:

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

Nest中间件是一个函数,或者是一个带有@Injectable()装饰器的类。该类应该实现NestMiddleware接口,而函数没有任何特殊要求。让我们从LoggerMiddleware示例开始。

logger.middleware.ts

JS

代码语言:javascript
复制
import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  resolve(...args: any[]): MiddlewareFunction {
    return (req, res, next) => {
      console.log('Request...');
      next();
    };
  }
}

resolve()方法必须返回常规的库特定中间件(req, res, next) => any

依赖注入

对于中间件,没有例外。与提供者和控制器相同,他们能够注入属于同一模块的依赖关系(通过constructor)。

应用中间件

@Module()装饰器中没有中间件的地方。我们必须使用configure()模块类的方法来设置它们。包含中间件的模块必须实现该NestModule接口。我们LoggerMiddleware在这个ApplicationModule级别设置。

app.module.ts

JS

代码语言:javascript
复制
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';

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

在上面的例子中,我们已经建立了LoggerMiddleware用于/cats我们先前内部的定义路径处理程序CatsController。此外,我们可能会将中间件限制为特定的请求方法。

app.module.ts

JS

代码语言:javascript
复制
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'cats', method: RequestMethod.GET });
  }
}

路由通配符

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

代码语言:javascript
复制
forRoutes({ path: '*', method: RequestMethod.ALL })

上述路线路径匹配abcdab_cdabecd,等等。字符?+*,和()是他们的正则表达式的对应的子集。连字符(-)和点(.)按字面顺序由基于字符串的路径解释。

中间件消费者

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

app.module.ts

JS

代码语言:javascript
复制
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(CatsController);
  }
}

提示apply()方法可以采用单个中间件,也可以采用中间件阵列

在使用课程时,我们通常可能想要排除某些路线。由于该exclude()方法,这非常直观。

app.module.ts

JS

代码语言:javascript
复制
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .exclude(
        { path: 'cats', method: RequestMethod.GET },
        { path: 'cats', method: RequestMethod.POST },
      )
      .forRoutes(CatsController);
  }
}

因此,LoggerMiddleware将限制在内部定义的所有路由,CatsController除了这两个传递给exclude()函数。请注意,该exclude()方法不适用于您的功能中间件。此外,此功能不排除来自更通用路由(例如通配符)的路径。在这种情况下,您应该将路径限制逻辑直接放在中间件上,例如,比较请求的URL。

可配置的中间件

有时,中间件的行为取决于自定义值,例如用户角色数组,选项对象等。我们可以resolve()使用该with()方法应用其他参数。请参阅以下示例:

app.module.ts

JS

代码语言:javascript
复制
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller';

@Module({
  imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .with('ApplicationModule')
      .forRoutes(CatsController);
  }
}

我们已通过普通字符串- ApplicationModulewith()方法。此后,我们要调整resolve()方法了LoggerMiddleware

logger.middleware.ts

JS

代码语言:javascript
复制
import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  resolve(name: string): MiddlewareFunction {
    return (req, res, next) => {
      console.log(`[${name}] Request...`); // [ApplicationModule] Request...
      next();
    };
 }
}

在这种情况下,name属性的值将是'ApplicationModule'

异步中间件

没有禁忌症会妨碍我们asyncresolve()方法中返回功能。此外,也可以制作该resolve()方法async。这种常见模式称为deferred middleware

logger.middleware.ts

JS

代码语言:javascript
复制
import { Injectable, NestMiddleware, MiddlewareFunction } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  async resolve(name: string): Promise<MiddlewareFunction> {
    await someAsyncJob();

    return async (req, res, next) => {
      await someAsyncJob();
      console.log(`[${name}] Request...`); // [ApplicationModule] Request...
      next();
    };
 }
}

功能中间件

LoggerMiddleware很短。它没有成员,没有其他方法,没有依赖。为什么我们不能只使用一个简单的功能?这是一个很好的问题,事实上 - 我们可以。这种类型的中间件称为功能中间件。让我们将记录器转换为函数。

logger.middleware.ts

JS

代码语言:javascript
复制
export function logger(req, res, next) {
  console.log(`Request...`);
  next();
};

并在以下内容中使用它ApplicationModule

app.module.ts

JS

代码语言:javascript
复制
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { logger } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller';

@Module({
  imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(logger)
      .forRoutes(CatsController);
  }
}

提示每当中间件不需要任何依赖项时,我们就考虑使用功能中间件

多个中间件

如前所述,为了绑定顺序执行的多个中间件,我们可以在apply()方法内部用逗号分隔它们。

JS

代码语言:javascript
复制
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(cors(), helmet(), logger)
      .forRoutes(CatsController);
  }
}

全局中间件

为了将中间件同时绑定到每个注册路由,我们可以利用实例use()提供的方法INestApplication

代码语言:javascript
复制
const app = await NestFactory.create(ApplicationModule);
app.use(logger);
await app.listen(3000);

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com