express+tsyringe访问接口报错Cannot read properties of undefined
问题描述
我正在开发学校课程的小型项目,是express与依赖注入技术的初学者,搭建该测试项目是为了熟悉相关技术的运行逻辑,花费数小时排查报错始终未定位到根因。
服务启动后访问根路由时抛出如下错误:
TypeError: Cannot read properties of undefined (reading 'service') at controllerMethod (D:\Projects\Typescript\third\dist\app.controller.js:20:14) at Layer.handle [as handle_request] (D:\Projects\Typescript\third\node_modules\express\lib\router\layer.js:95:5) at next (D:\Projects\Typescript\third\node_modules\express\lib\router\route.js:144:13) at Route.dispatch (D:\Projects\Typescript\third\node_modules\express\lib\router\route.js:114:3) at Layer.handle [as handle_request] (D:\Projects\Typescript\third\node_modules\express\lib\router\layer.js:95:5) at D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:284:15 at Function.process_params (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:346:12) at next (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:280:10) at Function.handle (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:175:3) at router (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:47:12)
已按照tsyringe要求配置装饰器、引入reflect-metadata、通过容器解析控制器实例,仍触发上述报错。
相关代码
app.ts
import express, { json, urlencoded } from 'express' import { APPROUTE } from './app.route' const app = express() app.use(json()) app.use(urlencoded({ extended: false })) app.use(APPROUTE) app.listen(5000, ()=>{ console.log(`connected on port 5000`) })
app.controller.ts
import { Request, Response } from "express"; import { inject, singleton } from "tsyringe"; import { AppService } from "./app.service"; @singleton() export class AppController{ constructor(@inject(AppService) private service: AppService){} controllerMethod(req: Request, res: Response){ this.service.methodService() } }
app.service.ts
import { singleton } from "tsyringe"; @singleton() export class AppService{ methodService(){ console.log('service working') } }
app.route.ts
import 'reflect-metadata' import { Router } from "express"; import { container } from "tsyringe"; import { AppController } from "./app.controller"; export const APPROUTE = Router() const controller: AppController = container.resolve<AppController>(AppController) APPROUTE.get('/', controller.controllerMethod)
package.json
{ "scripts": { "start": "nodemon ./dist/app.js" }, "devDependencies": { "nodemon": "^2.0.16" }, "dependencies": { "@types/express": "^4.17.13", "@types/node": "^18.0.0", "dotenv": "^16.0.1", "express": "^4.18.1", "reflect-metadata": "^0.1.13", "tsyringe": "^4.7.0" } }
tsconfig.json
{ "compilerOptions": { "target": "es6", "experimentalDecorators": true, "emitDecoratorMetadata": true, "module": "commonjs", "rootDir": "./src", "outDir": "./dist", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "strictPropertyInitialization": false, "skipLibCheck": true } }
根因与修复方案
根因
报错是类方法作为回调传递时的this上下文丢失问题。直接将controller.controllerMethod作为处理函数传给Express路由时,Express触发回调不会绑定controller实例作为执行上下文,方法运行时内部this为undefined,因此无法读取挂载在this上的service属性。依赖注入本身的配置没有问题。
修复方法
任选一种方式修改路由注册逻辑即可:
- 方式1:显式绑定this上下文
将app.route.ts中的路由注册行修改为:APPROUTE.get('/', controller.controllerMethod.bind(controller)) - 方式2:用箭头函数包裹调用,保留实例上下文
将路由注册行修改为:APPROUTE.get('/', (req, res) => controller.controllerMethod(req, res))
额外配置优化
reflect-metadata需要在应用最入口文件的最顶部引入,建议将import 'reflect-metadata'移到app.ts的第一行,不要仅在路由文件中引入,避免后续模块加载时因元数据读取顺序问题导致依赖注入失效。
内容的提问来源于stack exchange,提问作者TheMlouden
相关产品推荐
相关产品推荐

