普通视图

发现新文章,点击刷新页面。
昨天 — 2026年1月31日首页

Nest 的中间件 Middleware ?

作者 前端付豪
2026年1月31日 17:30

新建项目

nest new middleware-demo

创建一个中间件

nest g middleware aaa --no-spec --flat

image.png

加下打印

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

@Injectable()
export class AaaMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => void) {
    console.log('brefore');
    next();
    console.log('after');
  }
}

在 Module 里这样使用

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AaaMiddleware } from './aaa.middleware';

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

跑起来看看

image.png

可以指定更精确的路由,添加几个 handler

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('hello')
  getHello(): string {
    console.log('hello');
    return this.appService.getHello();
  }

  @Get('hello2')
  getHello2(): string {
    console.log('hello2');
    return this.appService.getHello();
  }

  @Get('hi')
  getHi(): string {
    console.log('hi');
    return this.appService.getHello();
  }

  @Get('hi1')
  getHi1(): string {
    console.log('hi1');
    return this.appService.getHello();
  }
}

module 匹配更新下

import { AaaMiddleware } from './aaa.middleware';
import {
  MiddlewareConsumer,
  Module,
  NestModule,
  RequestMethod,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(AaaMiddleware)
      .forRoutes({ path: 'hello/*path', method: RequestMethod.GET });
    consumer
      .apply(AaaMiddleware)
      .forRoutes({ path: 'hi1', method: RequestMethod.GET });
  }
}

image.png

Nest 为什么要把 Middleware 做成 class 呢?

为了依赖注入!

通过 @Inject 注入 AppService 到 middleware 里

import { AppService } from './app.service';
import { Inject, Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class AaaMiddleware implements NestMiddleware {
  @Inject(AppService)
  private readonly appService: AppService;

  use(req: Request, res: Response, next: () => void) {
    console.log('brefore');
    console.log('-------' + this.appService.getHello());
    next();
    console.log('after');
  }
}

image.png

这就是 Nest 注入的依赖

昨天以前首页

Nest 和 Express 是什么关系?

作者 前端付豪
2026年1月30日 10:42

Express 是一个处理请求、响应的库,基础使用

const express = require('express')
const cookieParser = require('cookie-parser')
const cookieValidator = require('./cookieValidator')

const app = express()

async function validateCookies (req, res, next) {
  await cookieValidator(req.cookies)
  next()
}

app.use(cookieParser())

app.use(validateCookies)

app.use((err, req, res, next) => {
  res.status(400).send(err.message)
})

app.listen(3000)

通过 use 一个个中间件来处理请求、返回响应。

这种调用链叫做洋葱模型

image.png

基于中间件能完成各种功能。

但是 Express 只是一个处理请求的库,并没有提供组织代码的架构能力

企业级开发,我们会用对它封装了一层的库,比如 Nest

Nest 提供了 IOC、AOP 等架构特性,规定了代码组织的形式,而且对 websocket、graphql、orm 等各种方案都提供了开箱即用的支持

Node 写服务三个顺序

  • 直接使用 http、https 的模块
  • 使用 express、koa 这种库
  • 使用 Nest 这种企业级框架

Nest 相当于 java 中的 Spring

一、Nest 和 Express 的真实关系

1️⃣ Nest 默认就是用 Express 跑的

const app = await NestFactory.create(AppModule)

这行代码底层等价于:

  • 创建一个 Express 实例
  • Nest 把所有 Controller / Middleware / Guard / Pipe 等
  • 统一挂到 Express 上

Nest ≈ Express + 架构层 + DI + AOP

当然你也可以切换成 Fastify:

NestFactory.create(AppModule, new FastifyAdapter())

二、Nest 在 Express 之上到底封装了什么?

1️⃣ 路由层:从「函数」升级为「类 + 装饰器」

Express 写法

app.get('/users/:id', (req, res) => {
  res.send(req.params.id)
})

Nest 写法(本质还是 Express)

@Controller('users')
export class UserController {
  @Get(':id')
  getUser(@Param('id') id: string) {
    return id
  }
}

Nest 做了什么?

  • 装饰器收集元数据

  • 启动时:

    • 扫描所有 Controller
    • 生成路由映射
    • 最终调用的还是 expressRouter.get(...)

👉 你写的是“声明式路由”,Nest 帮你转成 Express 路由


2️⃣ 中间件体系:Express Middleware + 生命周期顺序

Nest 没有发明新的中间件模型,而是:

  • 完整兼容 Express middleware
  • 但多了一层 执行顺序管理

Nest 的完整请求链路(关键)

Express Middleware
   ↓
Guard(权限)
   ↓
Pipe(参数校验 / 转换)
   ↓
Interceptor(before)
   ↓
Controller
   ↓
Interceptor(after)
   ↓
Exception Filter

Express 里你只能靠「中间件顺序 + 约定」
Nest 里这是 框架级别保证的顺序


3️⃣ DI(依赖注入):Express 完全没有的能力

Express 的痛点

const userService = new UserService()
const orderService = new OrderService(userService)
  • 谁先创建?
  • 单例怎么管?
  • 测试怎么 mock?

Nest:DI 容器统一托管

@Injectable()
export class UserService {}

@Controller()
export class AppController {
  constructor(private readonly userService: UserService) {}
}

Nest 做了什么?

  • 扫描 @Injectable() 元数据
  • 分析 constructor 参数
  • 自动创建依赖图
  • 控制生命周期(singleton / request / transient)

👉 这是 Nest 和 Express 最大的本质差异


4️⃣ AOP 能力(Express 没有)

Nest 的这些东西,在 Express 里你只能“手搓”:

Nest 能力 作用
Guard 权限、登录态
Pipe DTO 校验、类型转换
Interceptor 日志、耗时、缓存
Filter 统一异常处理

而且可以 精确到方法级别

@UseGuards(LoginGuard)
@UseInterceptors(TimeInterceptor)
@Get()
getData() {}

Express:全局 or 路由级 middleware
Nest:方法级 AOP


5️⃣ 模块系统:解决 Express 项目后期失控的问题

Express 项目后期常见问题

  • routes / services 到处 import
  • 依赖关系混乱
  • 拆服务极痛苦

Nest 的 Module 设计

@Module({
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

Nest 在 Express 之上加了:

  • 模块边界
  • provider 可见性控制
  • 子模块隔离

👉 这是为“多人协作 + 大项目”准备的


6️⃣ 底层依然是 Express API

Nest 不封死 Express

import { Request, Response } from 'express'

@Get()
get(@Req() req: Request, @Res() res: Response) {
  res.send('ok')
}

你甚至可以直接用原生 middleware:

app.use((req, res, next) => {
  console.log(req.url)
  next()
})

三、总结

Express 是 HTTP 框架

Nest 是“后端应用架构框架”

Nest 用 Express 负责:

  • 路由
  • 请求 / 响应

Nest 自己负责:

  • 架构
  • 依赖注入
  • 模块化
  • AOP
  • 可测试性
  • 工程规范

四、什么时候该用谁?

用 Express 就够的场景

  • 小工具
  • BFF / 轻接口
  • 一个人维护
  • 生命周期短

用 Nest 更稳的场景(企业级)

  • 中大型后台
  • 多模块、多角色
  • 需要长期维护
  • 对结构、测试、扩展有要求

Module 和 Provider 循环依赖怎么办?

作者 前端付豪
2026年1月28日 11:13

A 引入B B引入A

新建个项目看下

nest new circular-reference -p npm

image.png

nest g module aaa 
nest g module bbb

image.png

image.png

启动后报错

image.png

如何解决这个问题? 先单独创建这两个 Module,然后再让两者关联起来,使用 forwardRef

image.png

image.png

再次启动不会报错

image.png

原因就在于 nest 会单独创建两个 Module,之后再把 Module 的引用转发过去

除了 Module ,provider 和 Service 也会存在 循环引用


nest g service ccc --no-spec --flat 
nest g service ddd --no-spec --flat

并且互相引用下


import { Injectable } from '@nestjs/common';
import { DddService } from './ddd.service';

@Injectable()
export class CccService {
  constructor(private dddService: DddService) {}

  ccc() {
    return 'ccc';
  }

  eee() {
    return this.dddService.ddd() + 'eee';
  }
}


import { Injectable } from '@nestjs/common';
import { CccService } from './ccc.service';

@Injectable()
export class DddService {
  constructor(private cccService: CccService) {}

  ddd() {
    return this.cccService.ccc() + 'ddd';
  }
}

app.service.ts 使用

import { Injectable } from '@nestjs/common';
import { CccService } from './ccc.service';
import { DddService } from './ddd.service';

@Injectable()
export class AppService {
  constructor(
    private cccService: CccService,
    private dddService: DddService,
  ) {}

  getHello(): string {
    return this.dddService.ddd() + this.cccService.eee();
  }
}

image.png

使用 forwardRef 解决

image.png

image.png

再次启动服务就没问题了

image.png

❌
❌