NestJS中服务跨模块注入后内部属性失效问题求助
NestJS嵌套Service调用时DAO内部属性失效的问题解决
问题场景
使用NestJS + MikroORM开发REST应用,采用整洁架构做模块抽象,出现以下异常:
- 控制器直接注入并调用Service A(IBusinessDao实现):完全正常
- 控制器调用Service B(AuthService),再由Service B调用Service A:Service A内部的
repo属性为undefined,抛出TypeError: Cannot read properties of undefined (reading 'repo')
依赖链简化:
Controller C <- Service B (AuthServiceModule) <- Service A (MikroDatabaseModule) : 失效 Controller C <- Service A (MikroDatabaseModule) : 正常
核心错误代码
AuthService中的问题代码
async createAuth(phone: string, userId: string): Promise<UserAuthEntity> { const payload = { sub: userId, phone: phone }; return this.jwtService .signAsync(payload) .then( (token) => new UserAuthEntity(0, userId, phone, token, Status.Normal), ) .then(this.dao.userAuth.create); // 问题根源 }
错误栈
TypeError: Cannot read properties of undefined (reading 'repo') at create (/home/serveradmin/mrmap/server/mainline/src/iac/database/mikro/dao/mikro-base.dao.ts:26:17) at processTicksAndRejections (node:internal/process/task_queues:95:5) at async AuthController.implByService (/home/serveradmin/mrmap/server/mainline/src/api/auth/auth.controller.ts:31:19)
问题原因
将this.dao.userAuth.create作为回调传给then时,函数的this指向丢失:此时create方法执行时,this不再指向MikroBaseDao实例,而是变为全局对象(严格模式下为undefined),导致无法访问this.repo。
而控制器中直接调用this.dao.userAuth.create(entity)时,通过实例调用方法,this会正确绑定到MikroBaseDao实例,因此能正常访问repo。
解决方案
方案1:用箭头函数包裹回调(最推荐)
通过箭头函数保留外层上下文的this,确保create方法的调用者是MikroBaseDao实例:
async createAuth(phone: string, userId: string): Promise<UserAuthEntity> { const payload = { sub: userId, phone: phone }; return this.jwtService .signAsync(payload) .then( (token) => new UserAuthEntity(0, userId, phone, token, Status.Normal), ) .then(entity => this.dao.userAuth.create(entity)); // 箭头函数绑定上下文 }
方案2:提前绑定方法的this指向
在MikroBusinessDao初始化MikroBaseDao时,手动把create方法绑定到实例:
@Injectable() export class MikroBusinessDao implements IBusinessDao, OnApplicationBootstrap { userAuth: IBaseDao<UserAuthEntity>; constructor( @InjectRepository(UserAuthEntity) private _authRepo: EntityRepository<UserAuthEntity>, ) {} onApplicationBootstrap() { const baseDaoInstance = new MikroBaseDao(this._authRepo); // 绑定create方法的this到baseDaoInstance this.userAuth = { ...baseDaoInstance, create: baseDaoInstance.create.bind(baseDaoInstance) } as IBaseDao<UserAuthEntity>; } }
方案3:将MikroBaseDao的方法改为箭头函数
箭头函数会自动捕获当前上下文的this,避免绑定丢失:
export class MikroBaseDao<T extends object> implements IBaseDao<T> { constructor(private repo: EntityRepository<T>) {} // 改为箭头函数 async create = (item: T): Promise<T> => { // 原方法逻辑,此时this.repo可正常访问 ... } }
验证
修改后重新启动应用,调用implByService方法即可正常执行,不会再抛出repo未定义的异常。
内容的提问来源于stack exchange,提问作者Li Zhenxin
相关产品推荐
相关产品推荐

