如何在TypeScript的Controller构造函数中注入Service?
问题描述
我正在用TypeScript开发一个简单的REST API,调用层级为:Controller > Service > Repository。我尝试了几种写法,但前两种总会报错,只有第三种能正常运行,想请教问题出在哪,最初的写法是否可行?
第一种写法(报错)
Controller
export class GetNurseController { constructor(private getNurseService: GetNurseService) {} async handle(req: Request, res: Response): Promise<Response> { try { const { id } = req.authrocket.currentUser; const user = await this.getNurseService.execute(id); return res.json({ user }); } catch (err: any) { return res.status(500).json({ err }); // 返回空err对象,但控制台打印getNurseService未定义错误 } } }
Router
const nurseRepository = new NurseRepository(); const getNurseService = new GetNurseService(nurseRepository); const getNurseController = new GetNurseController(getNurseService); const nurseRoutes = Router(); nurseRoutes.get('/', requireLogin, getNurseController.handle);
第二种写法(报错)
Controller
export class GetNurseController { public NurseRepository: INurseRepository; public getNurseService: GetNurseService; constructor() { this.nurseRepository = new NurseRepository(); this.getNurseService = new GetNurseService(this.nurseRepository); } async handle(req: Request, res: Response): Promise<Response> { try { const { id } = req.authrocket.currentUser; const user = await this.getNurseService.execute(id); return res.json({ user }); } catch (err: any) { return res.status(500).json({ err }); } } }
Router
const getNurseController = new GetNurseController(); const nurseRoutes = Router(); nurseRoutes.get('/', requireLogin, getNurseController.handle);
第三种写法(正常运行)
Controller
export class GetNurseController { async handle(req: Request, res: Response): Promise<Response> { try { const nurseRepository = new NurseRepository(); const getNurseService = new GetNurseService(nurseRepository); const { id } = req.authrocket.currentUser; const user = await getNurseService.execute(id); return res.json({ user }); } catch (err: any) { return res.status(500).json({ err }); } } }
Router
const getNurseController = new GetNurseController(); const nurseRoutes = Router(); nurseRoutes.get('/', requireLogin, getNurseController.handle);
问题原因与解决方案
1. 第一种写法的核心问题:this指向丢失
当你把getNurseController.handle直接作为路由处理函数传递时,handle方法内部的this不再指向GetNurseController实例,而是变成了路由回调的上下文(通常是req或undefined),所以会出现getNurseService未定义的错误。
修复方案(任选其一):
- 手动绑定
this:在Router中传递方法时绑定实例上下文:nurseRoutes.get('/', requireLogin, getNurseController.handle.bind(getNurseController)); - 用箭头函数包裹:通过箭头函数保留
this指向:nurseRoutes.get('/', requireLogin, (req, res) => getNurseController.handle(req, res)); - 在类中用箭头函数定义方法:直接让方法的
this绑定到实例:export class GetNurseController { constructor(private getNurseService: GetNurseService) {} handle = async (req: Request, res: Response): Promise<Response> => { try { const { id } = req.authrocket.currentUser; const user = await this.getNurseService.execute(id); return res.json({ user }); } catch (err: any) { return res.status(500).json({ err }); } } }
2. 第二种写法的问题:属性命名不一致
你在类中声明的属性是public NurseRepository: INurseRepository;(首字母大写),但构造函数中赋值的是this.nurseRepository = new NurseRepository();(首字母小写),导致this.getNurseService依赖的this.nurseRepository未定义,进而引发错误。
修复方案:
统一属性命名的大小写:
export class GetNurseController { public nurseRepository: INurseRepository; // 改为小写开头 public getNurseService: GetNurseService; constructor() { this.nurseRepository = new NurseRepository(); this.getNurseService = new GetNurseService(this.nurseRepository); } // 同时需解决this指向问题,参考第一种写法的修复方案 async handle(req: Request, res: Response): Promise<Response> { // ... 原有逻辑 } }
3. 关于最初写法的可行性
你的第一种写法完全可行且更符合依赖注入原则,它通过构造函数注入Service依赖,比第三种在方法内部实例化依赖的写法更利于测试和解耦。只要修复this指向的问题,就能正常运行。
第三种写法虽然能运行,但每次请求都会创建新的Repository和Service实例,存在性能损耗,且不利于单元测试(无法Mock依赖),不推荐长期使用。
内容的提问来源于stack exchange,提问作者Heitor Danilo
相关产品推荐
相关产品推荐

