欢迎光临
我们一直在努力

【2026前端转 AI 全栈指南】第 3 章:Nestjs框架全解(一)

之前的系列:

【2026前端转 AI 全栈指南】第 1 章:前言 · 后端架构 · 章节导览 我们讲了 为什么要做这套教程、v1 要跑通哪条链路。 【2026前端转 AI 全栈指南】第 2 章(上):开发环境准备 【2026前端转 AI 全栈指南】第 2 章(下):NestJS 项目创建 · MongoDB 配置 · 项目启动与调试

大家好,我又来了。

从 本章 开始,我们正式进入 NestJS 的学习。我会带大家 从 0 到 1 认识、理解并使用这个 Node.js 后端框架,为后面的 面试帮(AI 求职助手) 项目打下坚实基础。

【2026前端转 AI 全栈指南】第 2 章(下):NestJS 项目创建 · MongoDB 配置 · 项目启动与调试 我们已经把开发环境配好、项目跑起来了;本章的第一节我们不着急进入核心的概念,我们来观察一下全局,专注回答一件事:

NestJS 是怎么工作的?一次请求进来,代码按什么顺序执行?

本章内容较多,我会拆成 21 篇小文(3.0~3.20)逐步发布,每篇对应一个知识点,方便跟练、复习和检索。

学习建议:

  • 通读一遍 NestJS 官方文档(中英文均可)—— 官网当「字典」,教程当「路线 + 实战」

说明: 本教程 全程免费,代码 开源。若对你有帮助,欢迎 点赞、收藏、关注,你的支持是我持续更新的动力,谢谢!

前置要求: 已完成 【2026前端转 AI 全栈指南】第 2 章(下):NestJS 项目创建 · MongoDB 配置 · 项目启动与调试(NestJS 项目创建 + MongoDB 连接)。

本篇结构:

  • 先建立全景(启动 + 请求两条线)—— 本章核心
  • 再融合官网第一步(文件说明、main.ts、平台、运行、lint)
  • 最后把请求生命周期拆层讲透
  • 后面 3.1~3.20 每一节,都是在请求链路或启动链路上的 某一个环节 挖深。


    章节目录(3.0~3.20)

    节标题一句话说明
    3.0 第一步与 NestJS 全景 · 请求生命周期 启动流程 + 请求链路总图(本章开篇,必读)
    3.1 控制器 Controllers 路由、@Get / @Post、请求参数
    3.2 服务 Services 业务逻辑层、Controller 与 Service 分工
    3.3 模块与项目架构 Module 拆分、Feature Module 目录规范
    3.4 提供者 Providers 依赖注入、@Injectable() 基础
    3.5 中间件 Middleware 请求进入路由前的最外层处理
    3.6 拦截器 Interceptors 统一响应、计时、序列化入口
    3.7 管道与数据验证 Pipes DTO、ValidationPipe、入参校验
    3.8 守卫与权限控制 Guards 鉴权原理(JWT 完整实现见第 6 章)
    3.9 异常过滤器 Exception Filters 统一 400 / 401 / 500 错误格式
    3.10 配置管理 Configuration .env、@nestjs/config
    3.11 异步编程与 RxJS Promise / async 与 Observable 基础
    3.12 NestJS 最佳实践 代码规范、目录约定、常见反模式
    3.13 自定义装饰器 Custom Decorators @Public()、@CurrentUser() 等
    3.14 Swagger 与 API 文档 @nestjs/swagger 自动生成接口文档
    3.15 单元测试与 E2E Jest、@nestjs/testing、supertest
    3.16 文件上传与 Multer 单文件 / 多文件上传(为 PDF 章铺路)
    3.17 基础深化 · 动态模块与 DI 进阶 forRootAsync、作用域、生命周期钩子
    3.18 技术精选 · SSE · 日志 · CORS 流式响应、版本控制、跨域
    3.19 main.ts 全局装配 全局 Pipe / Filter / 前缀 / 优雅关闭
    3.20 第三章总结与知识点梳理 全章回顾 + 面试追问 20 题

    3.0.1 NestJS 如何工作(全景,本章最重要)

    NestJS 的工作可以分成 两条互不混淆的线:

    阶段什么时候发生你在想什么
    A. 应用启动 执行pnpm run start:dev 一次 Module 怎么装配?Mongo 怎么连?
    B. 处理请求 用户 / 前端每次 调 API Guard → Pipe → Controller 谁先谁后?

    全景图 A:应用启动(还没有 HTTP 请求)

    在这里插入图片描述

    这一阶段做完,服务器在等请求;还没有 Guard / Pipe 参与。

    全景图 B:一次 HTTP 请求(本章最要记牢)

    客户端例如 GET http://localhost:3001/health: 在这里插入图片描述

    Express → Nest 路由匹配 → AppController.health() → JSON 响应

    第 3 章后续会 一层层把 ①~⑦ 加上,链路会从「缩短版」长成「完整版」。

    一张表记住执行顺序(面试必背)

    顺序组件一句话
    1 Middleware 最外层,还不知道具体 Handler
    2 Guard 鉴权:让不让进
    3 Interceptor(前) 进方法前
    4 Pipe 校验/转换参数
    5 Controller 路由 handler,可调 Service
    6 Interceptor(后) 出方法后,改响应
    7 Exception Filter 有异常时统一处理(可打断上面任一步)

    口诀:「M-G-I-P-C-I-E」 → Middleware · Guard · Interceptor · Pipe · Controller · Interceptor · Exception Filter。

    和前端类比

    Nest 请求链路前端近似
    Middleware Nuxt 全局middleware/
    Guard 路由beforeEnter / 登录判断
    Pipe zod / 表单 validator
    Controller API Route handler
    Interceptor axios 响应拦截器
    Exception Filter 全局errorHandler

    3.0.2 初始文件是干什么的

    Nest CLI 创建的项目会生成一套 约定俗成的初始结构。官网第一步列出了这些文件——我们的 project/nestjs-demo/ 在此基础上多了 Config / Mongoose(第 2 章已加)。

    官网文件对照表

    文件名描述(官网)在本教程中
    main.ts 应用入口,用NestFactory 创建 Nest 实例 监听端口,后续加全局 Pipe 等
    app.module.ts 根模块,应用的装配中心 注册 Config、MongoDB、Controller、Service
    app.controller.ts 基本控制器,至少一条路由 /、/health
    app.service.ts 基本服务,至少一个方法 getHello()
    app.controller.spec.ts 控制器的单元测试 Jest,3.15 详讲

    CLI 鼓励 每个功能模块放在独立目录(如后来的 user/、resume/)。面试帮生产项目就是这样拆的;第 3 章前期仍用根目录几个文件打基础。

    目录速览

    project/nestjs-demo/
    ├── src/
    │ ├── main.ts
    │ ├── app.module.ts
    │ ├── app.controller.ts
    │ ├── app.controller.spec.ts
    │ └── app.service.ts
    ├── test/ # E2E 测试(官网也有)
    ├── .env
    ├── package.json # scripts: start / start:dev / lint / format
    └── nest-cli.json


    3.0.3 main.ts 与 NestFactory 启动应用

    官网 main.ts 核心如下(我们端口用 .env 里的 3001,与第 2 章一致):

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';

    async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    await app.listen(process.env.PORT ?? 3001);
    }
    bootstrap();

    NestFactory 在做什么?

    步骤说明
    NestFactory.create(AppModule) 以根模块 为入口,创建实现 INestApplication 的应用实例
    内部过程 解析@Module → 构建 依赖注入容器 → 实例化 Provider → 注册 Controller 路由 → 执行 OnModuleInit
    app.listen(port) 启动 HTTP 监听,此后 才进入 3.0.1 全景图 B

    INestApplication 还提供 useGlobalPipes、setGlobalPrefix、enableCors 等方法——3.19 main.ts 全局装配 专讲。

    官网提示:启动失败时的行为

    默认情况下,创建应用若发生错误,进程以 退出码 1 结束。若希望抛出错误而不是静默退出:

    const app = await NestFactory.create(AppModule, { abortOnError: false });

    本地开发一般保持默认即可;CI 里常用退出码判断构建是否成功。


    3.0.4 五个文件如何协作(以 GET / 为例)

    把 启动 和 一次请求 连起来看。访问 GET http://localhost:3001/ 时:

    1. app.module.ts —— 装配(启动阶段)

    @Module({
    imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    MongooseModule.forRootAsync({
    imports: [ConfigModule],
    inject: [ConfigService],
    useFactory: (config: ConfigService) => ({
    uri: config.get<string>('MONGODB_URI'),
    }),
    }),
    ],
    controllers: [AppController], // 注册路由
    providers: [AppService], // 注册可注入的服务
    })
    export class AppModule {}

    Module = 声明「这个应用有哪些 Controller、Service、imports 子模块」。

    2. app.controller.ts —— 接 HTTP(请求阶段 · 第 ⑤ 步)

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

    @Get()
    getHello(): string {
    return this.appService.getHello();
    }

    @Get('health')
    health() {
    return {
    service: 'ai-interview-lab-api',
    ok: true,
    ts: Date.now(),
    };
    }
    }

    • @Controller() → 该类处理 HTTP
    • @Get() / @Get('health') → 路径 / 与 /health
    • constructor 注入 AppService → 不用 new AppService()

    3. app.service.ts —— 业务(被 Controller 调用)

    @Injectable()
    export class AppService {
    getHello(): string {
    return 'Hello World!';
    }
    }

    原则: Controller 薄,Service 厚。/health 暂时直接写在 Controller 里是为了简单;以后 Mongo / AI 逻辑都进 Service。

    4. app.controller.spec.ts —— 单元测试(开发阶段,非运行时)

    describe('AppController', () => {
    it('should return "Hello World!"', () => {
    expect(appController.getHello()).toBe('Hello World!');
    });
    });

    测试 不走 HTTP 链路,直接测 Controller 方法;请求生命周期里的 Guard / Pipe 在 3.15 单独 mock。

    协作关系图

    #mermaid-svg-vZfqw4gbuYB6SwvS{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vZfqw4gbuYB6SwvS .error-icon{fill:#552222;}#mermaid-svg-vZfqw4gbuYB6SwvS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vZfqw4gbuYB6SwvS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vZfqw4gbuYB6SwvS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vZfqw4gbuYB6SwvS .marker.cross{stroke:#333333;}#mermaid-svg-vZfqw4gbuYB6SwvS svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vZfqw4gbuYB6SwvS p{margin:0;}#mermaid-svg-vZfqw4gbuYB6SwvS .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-vZfqw4gbuYB6SwvS .cluster-label text{fill:#333;}#mermaid-svg-vZfqw4gbuYB6SwvS .cluster-label span{color:#333;}#mermaid-svg-vZfqw4gbuYB6SwvS .cluster-label span p{background-color:transparent;}#mermaid-svg-vZfqw4gbuYB6SwvS .label text,#mermaid-svg-vZfqw4gbuYB6SwvS span{fill:#333;color:#333;}#mermaid-svg-vZfqw4gbuYB6SwvS .node rect,#mermaid-svg-vZfqw4gbuYB6SwvS .node circle,#mermaid-svg-vZfqw4gbuYB6SwvS .node ellipse,#mermaid-svg-vZfqw4gbuYB6SwvS .node polygon,#mermaid-svg-vZfqw4gbuYB6SwvS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vZfqw4gbuYB6SwvS .rough-node .label text,#mermaid-svg-vZfqw4gbuYB6SwvS .node .label text,#mermaid-svg-vZfqw4gbuYB6SwvS .image-shape .label,#mermaid-svg-vZfqw4gbuYB6SwvS .icon-shape .label{text-anchor:middle;}#mermaid-svg-vZfqw4gbuYB6SwvS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vZfqw4gbuYB6SwvS .rough-node .label,#mermaid-svg-vZfqw4gbuYB6SwvS .node .label,#mermaid-svg-vZfqw4gbuYB6SwvS .image-shape .label,#mermaid-svg-vZfqw4gbuYB6SwvS .icon-shape .label{text-align:center;}#mermaid-svg-vZfqw4gbuYB6SwvS .node.clickable{cursor:pointer;}#mermaid-svg-vZfqw4gbuYB6SwvS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vZfqw4gbuYB6SwvS .arrowheadPath{fill:#333333;}#mermaid-svg-vZfqw4gbuYB6SwvS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vZfqw4gbuYB6SwvS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vZfqw4gbuYB6SwvS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vZfqw4gbuYB6SwvS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vZfqw4gbuYB6SwvS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vZfqw4gbuYB6SwvS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vZfqw4gbuYB6SwvS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vZfqw4gbuYB6SwvS .cluster text{fill:#333;}#mermaid-svg-vZfqw4gbuYB6SwvS .cluster span{color:#333;}#mermaid-svg-vZfqw4gbuYB6SwvS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vZfqw4gbuYB6SwvS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vZfqw4gbuYB6SwvS rect.text{fill:none;stroke-width:0;}#mermaid-svg-vZfqw4gbuYB6SwvS .icon-shape,#mermaid-svg-vZfqw4gbuYB6SwvS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vZfqw4gbuYB6SwvS .icon-shape p,#mermaid-svg-vZfqw4gbuYB6SwvS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vZfqw4gbuYB6SwvS .icon-shape .label rect,#mermaid-svg-vZfqw4gbuYB6SwvS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vZfqw4gbuYB6SwvS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vZfqw4gbuYB6SwvS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vZfqw4gbuYB6SwvS :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    每次请求 GET /

    HTTP GET /

    AppController.getHello

    AppService.getHello

    Hello World!

    启动一次

    main.ts

    NestFactory.create

    AppModule

    AppService 实例化

    AppController 注册路由


    3.0.5 运行应用 · 热重载 · lint / format

    官网运行方式如下;本教程用 pnpm(与第 2 章一致)。

    启动

    cd project/api

    # 普通启动
    pnpm run start

    # 开发推荐:文件变更自动重新编译 + 重启
    pnpm run start:dev

    终端出现类似 Nest application successfully started 后,浏览器访问:

    • http://localhost:3001/ → 应看到 Hello World!
    • http://localhost:3001/health → JSON

    官网提示:SWC 加速构建(可选)

    若觉得编译慢,可尝试 SWC 构建器(需自行配置,非教程必需):

    pnpm run start — -b swc

    代码检查与格式化

    CLI 脚手架自带 ESLint + Prettier,适合 CI 与 Git hooks:

    # ESLint 检查并自动修复
    pnpm run lint

    # Prettier 格式化
    pnpm run format

    工具作用
    ESLint 查逻辑/风格问题(未使用变量、错误写法)
    Prettier 统一缩进、引号、换行

    不确定区别时:Prettier 管 长什么样,ESLint 管 对不对。


    3.0.6 平台:Express 与 Fastify

    Nest 设计为 平台无关:核心业务(Controller / Guard / Pipe)可复用,底层 HTTP 引擎可换。

    在这里插入图片描述

    平台包说明
    Express(默认) @nestjs/platform-express 生态最大,本教程与面试帮默认使用,无需额外配置
    Fastify @nestjs/platform-fastify 更高性能,需FastifyAdapter

    指定 Express 类型时,可访问平台专有 API(一般不必):

    import { NestExpressApplication } from '@nestjs/platform-express';

    const app = await NestFactory.create<NestExpressApplication>(AppModule);
    // app.useStaticAssets(…) 等 Express 专属方法

    Fastify 换法见官网;本教程 不换,知道即可。


    3.0.7 请求生命周期:各层职责与执行顺序

    3.0.1 已给全景图,这里 按层展开,并标明教程对应节。

    顺序组件干什么典型场景教程
    1 Middleware 路由匹配前,最外层 请求日志、原始 body 3.5
    2 Guard 能否 执行 handler JWT、@Roles() 3.8
    3 Interceptor 前 进入 handler 前 计时、改入参 3.6
    4 Pipe 校验/转换入参 ValidationPipe、DTO 3.7
    5 Controller 路由方法 @Get / @Post 3.1
    6 Interceptor 后 离开 handler 后 统一{ code, data } 3.6
    7 Exception Filter 捕获异常 401/400/500 统一 JSON 3.9

    Interceptor 分前后两段;Exception Filter 在抛错时插入,打断正常流程。

    面试帮生产项目走了哪些层?

    层级面试帮教程project/api 现状
    Guard JWT 保护简历、押题等 第 6 章加入
    Pipe 注册/登录 DTO 校验 3.7 加入
    Interceptor / Filter 统一响应与错误 3.6 / 3.9 加入
    Middleware 按需 3.5 加入

    3.0.8 动手走一遍:/ 与 /health

    启动

    cd project/api
    pnpm run start:dev

    测试根路由(走 Controller → Service)

    curl http://localhost:3001/

    期望:Hello World!

    链路(当前缩短版): Express → 匹配 @Get() → AppController.getHello() → AppService.getHello() → 文本响应

    测试 health(仅 Controller)

    curl http://localhost:3001/health

    期望 JSON:

    {
    "service": "ai-interview-lab-api",
    "ok": true,
    "ts": 1710000000000
    }

    链路: Express → @Get('health') → health() 直接 return 对象 → Nest 序列化为 JSON

    尚未经过 Guard / Pipe——这正是「最小 Nest 项目」的正常状态。

    可选:跑单元测试

    pnpm run test

    验证 app.controller.spec.ts 通过,建立「测试也是 Nest 工作流一部分」的意识。


    3.0.9 补充:模块生命周期钩子(别和请求链路混了)

    官网 Fundamentals · Lifecycle events 里的 OnModuleInit 等,属于 启动线(全景 A),不是 请求线(全景 B)。

    请求生命周期模块钩子OnModuleInit
    触发 每次 HTTP 启动时一次
    用途 鉴权、校验、路由 seed 数据、初始化配置

    决策:

    每次请求要做的事 → Guard / Pipe / Service 方法
    只在启动做一次 → OnModuleInit


    3.0.10 后续章节对照

    问题读哪一节
    @Get、@Body、路由参数 3.1 控制器
    业务逻辑放哪 3.2 服务
    拆UserModule / ResumeModule 3.3 模块
    依赖注入进阶 3.4 → 3.17
    请求链路 ①~⑦ 逐层实现 3.5~3.9
    全局main.ts 装配 3.19

    常见坑

  • 把启动和请求混为一谈 NestFactory.create 只在启动跑;Guard / Pipe 在 每次请求 跑。
  • 背错顺序 Guard 在 Pipe 之前(先鉴权,再校验 body)。
  • 在 Controller 写 Mongo / AI 应放 Service(3.2)。
  • 端口不一致 本教程默认 3001(.env);官网示例是 3000,以你项目为准。
  • OnModuleInit 当请求拦截器 每次请求 不会 进 onModuleInit。

  • 面试 1 分钟版

    Nest 启动时 main.ts 用 NestFactory.create(AppModule) 装配 Module、实例化 Provider、注册路由,再 listen 监听端口。 每次请求依次经过 Middleware、Guard、Interceptor、Pipe、Controller,异常由 Exception Filter 处理;当前最小项目只有 Express → Controller。 默认底层 Express,可换 Fastify。OnModuleInit 是启动钩子,不是请求链路的一环。


    本章面试追问

    1. 说清 Nest 的两条「工作线」。 启动线:main.ts → NestFactory → Module 装配 → listen。请求线:Middleware → Guard → Interceptor → Pipe → Controller → 响应 / Exception Filter。

    2. 一次请求的完整顺序? Middleware → Guard → Interceptor(前)→ Pipe → Controller → Interceptor(后);异常 → Exception Filter。

    3. NestFactory.create 和 app.listen 分别做什么? 前者创建应用、DI、注册路由;后者开始接受 HTTP 请求。

    4. Guard 和 Middleware 区别? Middleware 不知道具体 Handler;Guard 知道要访问哪个 Controller,适合 JWT 和 @Public()。

    5. 官网五个初始文件各干什么? main.ts 入口;app.module 根模块;app.controller 路由;app.service 业务;*.spec.ts 单元测试。


    本章完成清单

    • 能口述 启动全景 与 请求全景
    • 能背出请求顺序 M-G-I-P-C-I-E
    • 对照官网说清五个初始文件
    • pnpm run start:dev 成功
    • curl / 与 curl /health 符合预期
    • 可选:pnpm run lint / pnpm run test 跑通

    本章小结

    • 本章最重要: Nest 怎么启动 + 请求进来各层顺序——两张全景图。
    • 官网第一步 的文件、NestFactory、平台、运行、lint/format 已融合进教程,并统一到 project/api + pnpm。
    • 当前项目只有 缩短版请求链路;3.5~3.9 会补全 Middleware → Filter。
    • 下一节从请求链路第 ⑤ 步展开:3.1 · 控制器 Controllers。

    上一节:【2026前端转 AI 全栈指南】第 2 章(下):NestJS 项目创建 · MongoDB 配置 · 项目启动与调试 下一节: 敬请期待

    赞(0)
    未经允许不得转载:171主机测评 » 【2026前端转 AI 全栈指南】第 3 章:Nestjs框架全解(一)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址