乐趣区

NestJs学习之旅4中间件

欢迎持续关注 NestJs 之旅 系列文章

中间件

中间件是在路由处理程序 之前 调用的函数。中间件函数可以访问 请求 响应 对象。

使用过 koa 和 express 的朋友应该知道,中间件是一个很核心的功能,尤其是 koa,核心就是中间件,连路由功能都是由中间件提供的。

中间件可以提供以下功能:

  • 运行过程中执行任意代码
  • 对请求和响应进行更改
  • 结束本次请求的响应
  • 继续调用下一个中间件

示例

NestJs 使用 @Injectable() 来装饰中间件,被装饰的对象应该实现 NestMiddleware 接口。

以下是一个日志中间件的实现:

// log.middleware.ts
import {Injectable, NestMiddleware} from '@nestjs/common';
import {Request, Response} from 'express';

@Injectable()
export class LogMiddleware implements NestMiddleware {use(req: Request, resp: Response, next: Function) {console.log(`${req.method} ${req.path}`)
        next();}
}
// app.module.ts
import {Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
import {LogMiddleware} from './common/middleware/log.middleware';
import {UserModule} from './user/user.module';

@Module({imports: [UserModule],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LogMiddleware)
      .forRoutes('users');
  }
}

针对请求方法应用中间件

上面的简单示例中会对所有的 users 路由应用中间件,如果需要只对特定的请求方法,比如 GET 请求才应用中间件,可以使用以下方式:

import {Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
import {LogMiddleware} from './common/middleware/log.middleware';
import {UserModule} from './user/user.module';

@Module({imports: [UserModule],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LogMiddleware)
       .forRoutes({path: 'users', method: RequestMethod.GET});
  }
}

应用多个中间件

import {Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
import {LogMiddleware} from './common/middleware/log.middleware';
import {UserModule} from './user/user.module';

@Module({imports: [UserModule],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LogMiddleware, OtherMiddleware)
       .forRoutes({path: 'users', method: RequestMethod.GET});
  }
}

基于控制器名称应用中间件

上述代码都是针对固定的路由地址应用中间件,在 NestJs 中路由地址是通过装饰器定义的,如果控制器的路由地址有变化,而中间件这里没有跟着改掉,就会导致问题。

NestJs 在使用中间件的时候提供了基于控制器来注册的方式:

import {Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
import {LogMiddleware} from './common/middleware/log.middleware';
import {UserModule} from './user/user.module';

@Module({imports: [UserModule],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LogMiddleware)
       .forRoutes(UserController);
  }
}

排除指定路由

有些场景下对控制器应用了中间件之后需要绕过其中几个方法,比如登录验证中间件应该放行登录路由,否则没有人能够登录成功。

import {Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
import {LogMiddleware} from './common/middleware/log.middleware';
import {UserModule} from './user/user.module';

@Module({imports: [UserModule],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LogMiddleware)
      .exclude({path:'users/login',method:RequestMethod.GET}
      )
       .forRoutes(UserController);
  }
}

全局中间件

类似于全局模块,中间件也可以全局注册,对每一个路由都生效。

// main.ts
const app = await NestFactory.create(AppModule);
app.use(LogMiddleware);
await app.listen(3000);

结尾

中间件给框架赋予了极大的灵活性,可以根据功能抽象为中间件,达到”可插拔“的目的。

如果您觉得有所收获,分享给更多需要的朋友,谢谢!

如果您想交流关于 NestJs 更多的知识,欢迎加群讨论!

退出移动版