中间件是在路由处理程序 之前 调用的函数
中间件函数可以执行以下任务:
- 执行任何代码。
- 对请求和响应对象进行更改。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
- 如果当前的中间件函数没有结束请求-响应周期, 它必须调用
next()
将控制传递给下一个中间件函数。否则, 请求将被挂起。
创建中间件
使用 CLI
创建服务类,只需执行 $ nest g middleware logger
命令
nest g middleware logger
生成中间件文件,如下:
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('触发中间件');
next();
}
}
应用中间件
中间件不能在 @Module()
装饰器中列出。
必须使用模块类的 configure()
方法来设置它们。包含中间件的模块必须实现 NestModule
接口。
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserController } from './user/user.controller';
import { UserService } from './user/user.service';
import { UserModule } from './user/user.module';
import { LoggerMiddleware } from './logger/logger.middleware';
@Module({
imports: [UserModule],
controllers: [AppController, UserController],
providers: [AppService, UserService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('user/v3');
}
}
configure()
方法是 NestJS 中的一个生命周期方法,它实现了NestModule
接口,用于在模块加载时注册中间件/拦截器/守卫等。该方法会在应用程序启动时自动调用。
apply()
方法是MiddlewareConsumer
接口中的一个方法,用于将中间件注册到指定的路由上,返回一个MiddlewareConsumer
实例,可以继续链式调用其他方法。
forRoutes()
方法用于指定中间件要拦截的路由,接收一个对象参数,包含path
和method
两个属性,分别表示要拦截的路由路径和请求方法。只有当请求的路径和方法与指定的路由匹配时,中间件才会被触发。
路由是在控制器中使用的,不清楚控制器的同学,可以点击前往学习控制器
// 控制器
import { Controller, Get, Redirect } from '@nestjs/common';
import { AppService } from '../app.service';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
constructor(
private readonly appService: AppService,
private readonly userService: UserService,
) {}
@Get('/v1')
getRoute() {
return this.appService.getRoute();
}
@Get('/v2')
@Redirect('/user/v3')
getName() {
return this.userService.userName();
}
@Get('/v3')
getAge() {
return this.userService.getAge();
}
}
操作一、
因为上方代码中,只拦截user/v3
路由,所以访问http://127.0.0.1:3000/user/v3
地址,中间件成功拦截
并执行中间件中的方法
操作二、
因为上方代码中,只拦截user/v3
路由,所以访问http://127.0.0.1:3000/user/v1
地址,中间件不进行拦截
,所以终端没有打印
中间件还可以限制特定的请求方法,例如:
下面的代码中,访问http://127.0.0.1:3000/user/v3
地址,中间件只拦截Get请求
,其他形式的请求,不进行拦截
import { Controller, Get, Post, Redirect } from '@nestjs/common';
import { AppService } from '../app.service';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
constructor(
private readonly appService: AppService,
private readonly userService: UserService,
) {}
@Get('/v3')
getAge() {
return this.userService.getAge();
}
@Post('/v3')
getAge1() {
return this.userService.getAge();
}
}
中间件拦截控制器
中间件不仅可以拦截直接定的路由地址,还可以拦截控制器(拦截该控制器中所有请求)
例如:下方代码中,中间件拦截UserController
控制器中所有请求
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserController } from './user/user.controller';
import { UserService } from './user/user.service';
import { UserModule } from './user/user.module';
import { LoggerMiddleware } from './logger/logger.middleware';
@Module({
imports: [UserModule],
controllers: [AppController, UserController],
providers: [AppService, UserService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes(UserController);
}
}
路由通配符
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
以上路由地址将匹配
abcd
、ab_cd
、abecd
等
中间件消费者(管理中间件)
管理中间件的核心就是MiddlewareConsumer
类,提供的几种内置方法来管理中间件。
exclude()方法
:表示在exclude
方法中的访问地址,不进行拦截,使管理更加灵活
// app.module.ts
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserController } from './user/user.controller';
import { UserService } from './user/user.service';
import { UserModule } from './user/user.module';
import { LoggerMiddleware } from './logger/logger.middleware';
@Module({
imports: [UserModule],
controllers: [AppController, UserController],
providers: [AppService, UserService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'user/v1', method: RequestMethod.GET },
{ path: 'user/v2', method: RequestMethod.GET },
)
.forRoutes(UserController);
}
}
// user.controller.ts
import { Controller, Get, Post, Redirect } from '@nestjs/common';
import { AppService } from '../app.service';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
constructor(
private readonly appService: AppService,
private readonly userService: UserService,
) {}
@Get('/v1')
getRoute() {
return this.appService.getRoute();
}
@Get('/v2')
getName() {
return this.userService.userName();
}
@Get('/v3')
getAge() {
return this.userService.getAge();
}
@Post('/v3')
getAge1() {
return this.userService.getAge();
}
}
上方两段代码中,访问
user/v1
和user/v2
地址,中间件不拦截;访问user/v3
地址,中间件进行拦截
函数式中间件
创建一个函数
// logger.middleware.ts
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
注册到apply
中
// app.module.ts
consumer
.apply(logger)
.forRoutes(CatsController);
注意:
中间件没有任何依赖关系时,可以考虑使用函数式中间件
全局中间件
想一次性将中间件绑定到每个注册路由
通过use()
方法,在main.ts
文件进行注册中间件,这样每次请求都会经过这个中间件
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);