Nest中间件实际上等价express中间件,其调用时机是在路由程序处理之前进行的处理。中间件函数可以访问请求和响应对象以及next()中间件函数。
如果当前的中间件函数没有结束请求-响应周期,它必须调用next()将控制传递给下一个中间件函数。否则, 请求将被挂起。
在函数中或在具有 @Injectable()装饰器的类中实现自定义Nest中间件。这个类应该实现NestMiddleware 接口, 而函数没有任何特殊的要求。
我们可以通过cli命令创建中间件:
PS E:\ms> nest generate mi ./middleware/log
CREATE /src/middleware/log.middleware.spec.ts (176 bytes)
CREATE /src/middleware/log.middleware.ts (195 bytes)

生成的文件如下所示:
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LogMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
next();
}
}
这里我们简单打印一句log,然后修改req和res的类型为Request和Response:
use(req: Request, res: Response, next: () => void) {
console.log("log middleware...",req.url)
next();
}
接下来我们需要在我们的Module中使用中间件,中间件不能在@Module()装饰器中列出。我们必须使用模块类的configure()方法来设置它们。包含中间件的模块必须实现 NestModule 接口:
export class UserModule implements NestModule{
configure(consumer: import("@nestjs/common").MiddlewareConsumer) {
consumer.apply(LogMiddleware).forRoutes('');
}
}
其中apply函数中使用逗号分隔我们需要使用的多个中间件,forRoutes函数中指定的是针对哪个路由。
如果我们使用空值,那么这里会将模块中定义的路由全部使用中间件:
log middleware... /user/create
log middleware... /user/
这里我们可以指定具体的路由,比如create,此时中间件将只会对create路由使用。
另外,如果我们需要对特定的请求方法进行限制,那么我们同时指定path以及方法:
export class UserModule implements NestModule {
configure(consumer: import("@nestjs/common").MiddlewareConsumer) {
consumer.apply(LogMiddleware).forRoutes({ path: 'user/create', method: RequestMethod.POST });
}
}
注意这里的路径请使用全路径,不然中间件不会正确的设置。
在这个基础上,我们可以使用exclude来排除我们不期望的路由,那么可以使用如下格式:
configure(consumer: import("@nestjs/common").MiddlewareConsumer) {
consumer
.apply(LogMiddleware)
.exclude('user/create')
//.exclude({ path: 'user/create', method: RequestMethod.POST })
.forRoutes(UserController);
}
注意:此方法不排除来自更通用路由(例如,通配符)的路径以及函数中间件
同样的,我们可以通过通配符来进行设置,比如:
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
MiddlewareConsumer 是一个帮助类。它提供了几种内置方法来管理中间件。他们都可以被简单地链接起来。forRoutes() 可接受一个字符串、多个字符串、对象、一个控制器类甚至多个控制器类。在大多数情况下,可能只会传递一个由逗号分隔的控制器列表。就如上面的例子:
.forRoutes(UserController,...);
我们使用的LogMiddleware没有成员,没有额外的方法,没有依赖关系,此时我们可以将其作为一个简单函数导出,这种类型的中间件我们称之为函数中间件:
export function logger(req,res,next){
console.log("log middleware...",req.url);
next();
}
其使用和上面相同,另外我们在apply中指定的中间件是顺序执行的。
最后我们再来看看怎么在全局使用函数中间件,这里只需要在main.ts中使用use方法:
app.use(LogMiddleware);