nestjs学习 - 控制器、提供者、模块

一、控制器(Controller)

控制器负责处理传入的请求 并向客户端返回响应

接收客户的请求,然后告诉 服务层该做什么,最后把结果端给客户。

对应请求路径的配置。

控制器不过多介绍,比较好理解,对应的规则可以查阅官方文档。

二、提供者(Provide)

提供者是Nest中的一个基本概念。

许多基本的Nest类可以被视为提供者 - 服务、存储库、工厂、助手等。提供者的主要思想是它可以作为依赖项注入

这意味着对象可以彼此之间建立各种关系,而对象实例的"连接"功能可以在很大程度上委托给Nest运行时系统。

1. 它是什么

提供者是普通的JavaScript类,在模块中声明为providers

Provider 是一个 可以被依赖注入系统 "提供"给其他类(通常是控制器 Controller 或其他服务 Service)的对象函数

当你在一个类(如 Controller)的构造函数中声明了一个依赖项时,NestJS 会去查找对应的 Provider 来注入这个依赖。

白话描述一:它是一个对象、值或函数,它可以通过 依赖注入 的方式,注入到系统中供其它类使用;

"Provider 是 NestJS IoC 容器管理的资源(可以是类实例、值或工厂函数),它作为依赖项,被自动注入到需要它的类中。"

2. 使用:

使用提供者通常分为三个步骤:定义注册注入

2.1 定义

通常使用 @Injectable() 装饰器标记一个类。

typescript 复制代码
import { Injectable } from '@nestjs/common';
​
@Injectable() // 这个装饰器告诉 NestJS,CatService 是一个提供者
export class CatService {
  private readonly cats = [];
​
  create(cat: any) {
    this.cats.push(cat);
  }
​
  findAll(): any[] {
    return this.cats;
  }
}

2.2 注册

必须在模块(Module)的 providers 数组中注册该提供者,这样它才能被该模块内的其他类发现。

typescript 复制代码
// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatService } from './cat.service';
​
@Module({
  controllers: [CatsController],
  providers: [CatService], // 在这里注册
})
export class CatsModule {}

2.3 注入

在构造函数中声明依赖,NestJS 会自动解析类型并注入实例。

typescript 复制代码
// cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CatService } from './cat.service';
​
@Controller('cats')
export class CatsController {
  // 通过构造函数注入
  constructor(
    private readonly catService: CatService
  ) {}
​
  @Post()
  create(@Body() createCatDto: any) {
    this.catService.create(createCatDto);
    return 'Action has been executed';
  }
​
  @Get()
  findAll() {
    return this.catService.findAll();
  }
}

2.4 自定义提供者

除了标准的类提供者,NestJS 还支持更灵活的自定义提供者,用于处理复杂的场景。

定义、注册过程
less 复制代码
const configServiceProvider = {
  provide: ConfigService,
  useClass:
    process.env.NODE_ENV === 'development'
      ? DevelopmentConfigService
      : ProductionConfigService,
};
​
@Injectable()
class LoggerService {
  /* implementation details */
}
​
@Module({
  providers: [
    // 一、普通提供者,或称为:类提供者
    configServiceProvider,
    // 二、值提供者示例
    {
      provide: 'AA',
      useValue: {
        a: 1,
        b: 2,
      },
    },
    // 三、工厂提供者示例:动态创建数据库配置
    {
      provide: 'DB_CONFIG',
      useFactory: (usersService: UsersService) => {
        // 工厂函数可以执行异步操作、条件判断等
        const env = process.env.NODE_ENV || 'development';
        return {
          host: env === 'production' ? 'prod.db.com' : 'localhost',
          port: 5432,
          database: 'users_db',
          timestamp: new Date().toISOString(),
          // 可以使用注入的依赖
          serviceName: usersService.constructor.name,
        };
      },
      inject: [UsersService], // 声明依赖项
    },
    // 四、异步工厂提供者示例
    {
      provide: 'ASYNC_CONNECTION',
      useFactory: async () => {
        // 模拟异步操作,如数据库连接
        await new Promise(resolve => setTimeout(resolve, 8000));
        return {
          status: 'connected',
          connectionId: Math.random().toString(36).substring(7),
          connectedAt: new Date().toISOString(),
        };
      },
    },
    
    // 别名提供者: useExisting 
    LoggerService,
    {
      provide: 'AliasedLoggerService',
      useExisting: LoggerService,
    },
    
  ],
})
注入方式:
less 复制代码
@Controller('users')
export class UsersController {
  constructor(
    @Inject('AA') private readonly aa: any,
    @Inject('DB_CONFIG') private readonly dbConfig: any,
    @Inject('ASYNC_CONNECTION') private readonly connection: any,
  ) {
    // 在构造函数中可以看到工厂提供者创建的实例
    console.log('数据库配置:', this.dbConfig);
    console.log('异步连接:', this.connection);
  }
}

三、模块(module)

在 NestJS 框架中,@Module() 装饰器是构建应用程序架构的基石。NestJS 的整个应用结构就是由一个个模块组成的树状结构。

以下是对 @Module() 的详细介绍,包括它的定义、作用、核心属性以及使用示例。

1. 它是什么

@Module() 是一个 TypeScript 装饰器(Decorator),用于定义一个 NestJS 模块

在 NestJS 中,模块是组织代码的基本单元。每个 NestJS 应用程序至少有一个模块(通常是 AppModule),作为应用程序的根模块。通过模块,NestJS 能够利用依赖注入(Dependency Injection)系统来管理类之间的依赖关系。

2. 它的作用是什么?

@Module() 的主要作用是定义上下文边界组织依赖关系。它告诉 NestJS 编译器:

  • 这个模块包含哪些提供者(Services/Providers)。
  • 这个模块向外暴露哪些提供者供其他模块使用。
  • 这个模块需要导入哪些其他模块。
  • 这个模块包含哪些控制器(Controllers)。

简单来说,它解决了代码的高内聚、低耦合问题,让大型项目的结构清晰可见。

3. 它的核心属性

@Module() 装饰器接收一个配置对象,该对象包含四个主要属性:

属性 类型 描述
providers Provider[] 提供者数组。这里定义的服务(Service)、仓库(Repository)或其他可注入的类,将在当前模块内通过依赖注入可用。
exports Provider[] 导出数组 。默认情况下,模块内的提供者只能在模块内部使用。如果想让其他模块也能使用这些提供者,必须在这里列出它们。
imports Module[] 导入数组。列出当前模块所依赖的其他模块。导入后,当前模块可以使用那些被导出模块中的提供者。
controllers Controller[] 控制器数组。定义属于该模块的控制器,用于处理 HTTP 请求并返回响应。

4. 使用

假设我们要构建一个简单的用户管理系统,包含 UsersModule(用户模块)和 DatabaseModule(数据库模块)。

场景 A:定义一个基础模块 (UsersModule)

typescript 复制代码
// users/users.service.ts
import { Injectable } from '@nestjs/common';
​
@Injectable()
export class UsersService {
  findAll() {
    return [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
  }
}
​
// users/users.controller.ts
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';
​
@Controller('users')
export class UsersController {
  // 依赖注入 UsersService
  constructor(private readonly usersService: UsersService) {}
​
  @Get()
  findAll() {
    return this.usersService.findAll();
  }
}
​
// users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
​
@Module({
  // 1. 注册控制器,处理 /users 路由
  controllers: [UsersController],
  
  // 2. 注册服务,使其可在本模块内注入
  providers: [UsersService],
  
  // 3. 如果其他模块想用 UsersService,需在此导出
  exports: [UsersService], 
  
  // 4. 如果依赖其他模块(如数据库),在此导入
  imports: [], 
})
export class UsersModule {}

场景 B:模块间的依赖 (导入与导出)

假设我们有一个 DatabaseModule 提供了一个 DbConnection 服务,并且我们希望 UsersModule 能使用它。

typescript 复制代码
// database/database.service.ts
import { Injectable } from '@nestjs/common';
​
@Injectable()
export class DbConnection {
  connect() {
    console.log('Connected to DB');
  }
}
​
// database/database.module.ts
import { Module } from '@nestjs/common';
import { DbConnection } from './database.service';
​
@Module({
  providers: [DbConnection],
  // 关键点:必须导出,其他模块才能用
  exports: [DbConnection], 
})
export class DatabaseModule {}

现在,修改 UsersModule 来使用它:

typescript 复制代码
// users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { DatabaseModule } from '../database/database.module'; // 引入模块定义
​
@Module({
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
  // 关键点:导入 DatabaseModule,这样 UsersService 就可以注入 DbConnection 了
  imports: [DatabaseModule], 
})
export class UsersModule {}

users.service.ts

typescript 复制代码
// users/users.service.ts (更新后)
import { Injectable } from '@nestjs/common';
import { DbConnection } from '../database/database.service';

@Injectable()
export class UsersService {
  // 现在可以注入了,因为 UsersModule 导入了 DatabaseModule 且后者导出了 DbConnection
  constructor(private readonly db: DbConnection) {
    this.db.connect();
  }
  
  findAll() {
    return [{ id: 1, name: 'Alice' }];
  }
}

5. 最佳实践与注意事项

  1. 根模块 (Root Module) :每个应用都有一个根模块(通常在 app.module.ts),它是模块树的入口。NestJS 从根模块开始解析依赖图。
  2. 不要循环依赖 :尽量避免模块 A 导入模块 B,同时模块 B 又导入模块 A。如果必须这样做,需要使用 forwardRef(() => ModuleB)
  3. 按需导出:只导出其他模块真正需要的提供者。这有助于隐藏内部实现细节,保持封装性。
  4. 功能内聚:一个模块应该专注于单一的业务领域(例如:用户模块只处理用户相关逻辑,订单模块只处理订单逻辑)。
  5. 全局模块 :如果一个模块(如配置模块、日志模块)需要在几乎所有地方使用,可以使用 @Global() 装饰器将其标记为全局模块,这样就不需要在每个模块的 imports 数组中重复导入它。

6. 总结

@Module() 是 NestJS 的骨架。

  • Controllers 处理请求。
  • Providers 处理业务逻辑。
  • Imports/Exports 处理模块间的通信。
  • @Module() 将它们捆绑在一起,形成一个有机的整体。
相关推荐
优秀稳妥的JiaJi7 小时前
基于腾讯地图实现电子围栏绘制与校验
前端·vue.js·前端框架
San307 小时前
手写 Mini Cursor:基于 Node.js 与 LangChain 的开发实战
langchain·node.js·agent
前端开发呀7 小时前
从 qiankun(乾坤) 迁移到 Module Federation(模块联邦),对MF只能说相见恨晚!
前端
没想好d8 小时前
通用管理后台组件库-10-表单组件
前端
恋猫de小郭8 小时前
你用的 Claude 可能是虚假 Claude ,论文数据告诉你,Shadow API 中的欺骗性模型声明
前端·人工智能·ai编程
_Eleven8 小时前
Pinia vs Vuex 深度解析与完整实战指南
前端·javascript·vue.js
cipher8 小时前
HAPI + 设备指纹认证:打造更安全的远程编程体验
前端·后端·ai编程
WeNTaO8 小时前
ACE Engine FrameNode 节点
前端
郑鱼咚9 小时前
现在的AI热潮,恰恰证明了这个世界就是个草台班子
前端·人工智能·程序员