likes
comments
collection
share

Nest.js 从零到壹详细系列(六):中间件1. 基本概念 中间件是Express很强大的一个功能,提供了丰富的扩展性

作者站长头像
站长
· 阅读数 53

1. 基本概念

中间件是Express很强大的一个功能,提供了丰富的扩展性,所有的逻辑处理都通过中间件实现。任何一个中间件都可以对request和response对象访问和存取,再用next()函数将控制权交给下一个中间件。

NestJS 使用 Express.js 作为其默认的 HTTP 服务器实现。这意味着 NestJS 应用程序底层仍然可以访问所有 Express.js 提供的功能,例如中间件。

之前介绍的过滤器和拦截器的底层还是express的中间件概念,只不过在Nest中,根据使用场景对中间件进行概念拆分:拦截器注重请求的全生命周期控制,例如更改返回的数据格式;过滤器只关注异常情况,例如异常日志记录;中间件原则不控制请求源到目标的数据,只做翻译、转换。例如某个通用的逻辑,被很多API用到,那就可以为这一批API设置中间件。类似于hooks,都是逻辑抽离与复用;

2. 基本使用

1. 命令行快捷创建中间件

nest g mi core/middleware/Logger --no-spec --flat

2. 定义中间件,修改logger.middleware.ts

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();
  }
}

req和res分别对应请求和响应对象,next()函数是将控制权交给下一个。

3. 注册,修改app.module.ts

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from 'src/core/middleware/logger.middleware';

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

中间件不能在 @Module() 装饰器中列出。我们必须使用模块类的 configure() 方法来设置它们

configure 方法接受一个 MiddlewareConsumer 类型的参数。MiddlewareConsumer 是一个可以用来注册中间件的对象

apply方法注册要应用的中间件

forRoutes方法指定了中间件应该应用于哪些路由

在forRoutes中,除了定义中间件应用的路由,还可以进一步将中间件限制为特定的请求方法。修改app.module.ts

//...省略
export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
      consumer
        .apply(LoggerMiddleware)
        .forRoutes({ path: '/', method: RequestMethod.GET });
    }
  }

3. 路由通配符

路由同样支持模式匹配。例如,星号被用作通配符,将匹配任何字符组合。

forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });

以上路由地址将匹配 abcd 、 ab_cd 、 abecd 等。字符 ? 、 + 、 * 以及 () 是它们的正则表达式对应项的子集。连字符 (-) 和点 (.) 按字符串路径解析。

4. 排除某些路由

forRoutes可以定义中间件应用哪些路由,exclude可以定义中间件不能应用哪些路由

consumer
  .apply(LoggerMiddleware)
  .exclude('cats/(.*)')
  .forRoutes('/');

5. 应用控制器

forRoutes可接受一个字符串、多个字符串、对象、一个控制器类或多个控制器类。在大多数情况下,只需传递一个由逗号分隔的控制器列表。

//...
export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
      consumer
        .apply(LoggerMiddleware)
        .forRoutes(AppController,CatsController);
    }
  }

6. 多个中间件

1. 如果多个中间件有相同的作用域,则可将多个中间件一同注册到模块中,使用逗号分隔。

export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
      consumer
        .apply(LoggerMiddleware,ReqMiddleware)
        .forRoutes('/');
    }
  }

2. 如果中间件有不同的作用域,可通过链式调用,分别注册。

export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
      consumer
        .apply(LoggerMiddleware)
        .forRoutes({path:'/',method:RequestMethod.GET})
        .apply(ReqMiddleware)
        .forRoutes({path:'/user',method:RequestMethod.GET});
    }
  }

7. 函数中间件

除了以类方式声明的中间件,Nest也支持以函数方式定义中间件,logger.middleware.ts的内容等同于

export function logger(req, res, next) {
    console.log(`Request...`);
    next();
  };

当中间件没有任何依赖关系时,可以考虑使用函数式中间件。这也是Express中最常用的中间件定义形式。

结尾

项目地址:github.com/cwjbjy/nest…

后续会继续更新Nest系列文章,感兴趣的可先关注我。

转载自:https://juejin.cn/post/7414157132972851236
评论
请登录