如何在Express+TypeScript中通过Inversify实现路由依赖注入
在Express+TypeScript中用Inversify管理路由控制器实例
要实现由Inversify接管控制器的实例化,替代手动创建的方式,只需按以下步骤配置:
1. 基础依赖准备
确保已安装必要依赖:
npm install inversify reflect-metadata express @types/express typescript # 若使用Inversify Express工具库简化路由,额外安装: npm install inversify-express-utils
在项目入口文件(如app.ts)顶部导入反射元数据:
import "reflect-metadata";
2. 定义注入标识(Types)
创建types.ts文件,用Symbol定义唯一的注入标识,避免命名冲突:
export const TYPES = { ProductService: Symbol.for("ProductService"), ProductController: Symbol.for("ProductController") };
3. 配置Inversify容器
创建container.ts,绑定服务与控制器,让Inversify管理实例生命周期:
import { Container } from "inversify"; import { TYPES } from "./types"; import { ProductService } from "./ProductService"; import { ProductController } from "./ProductController"; const container = new Container(); // 绑定服务(示例为单例模式,可根据需求改为瞬态等) container.bind<ProductService>(TYPES.ProductService).to(ProductService).inSingletonScope(); // 绑定控制器 container.bind<ProductController>(TYPES.ProductController).to(ProductController); export { container };
4. 改造控制器(添加注入装饰器)
在ProductController.ts中,用@injectable()标记类,通过@inject()注入服务:
import { injectable, inject } from "inversify"; import { Request, Response } from "express"; import { ProductService } from "./ProductService"; import { TYPES } from "./types"; @injectable() export class ProductController { private readonly productService: ProductService; // 通过构造函数注入服务 constructor(@inject(TYPES.ProductService) productService: ProductService) { this.productService = productService; } // 路由处理方法 getProducts(req: Request, res: Response) { const products = this.productService.getAllProducts(); res.json(products); } getProductById(req: Request, res: Response) { const product = this.productService.getProductById(req.params.id); res.json(product); } }
5. 两种路由绑定方案
方案1:自定义路由(手动从容器获取控制器)
在Router.ts中,不再手动实例化控制器,而是从Inversify容器中获取实例:
import { Router } from "express"; import { container } from "./container"; import { ProductController } from "./ProductController"; import { TYPES } from "./types"; const router = Router(); // 从容器获取控制器实例 const productController = container.get<ProductController>(TYPES.ProductController); // 绑定路由到控制器方法 router.get("/products", (req, res) => productController.getProducts(req, res)); router.get("/products/:id", (req, res) => productController.getProductById(req, res)); export { router };
最后在app.ts中挂载路由:
import express from "express"; import { router as productRouter } from "./Router"; const app = express(); app.use(express.json()); app.use(productRouter); app.listen(3000, () => console.log("Server running on port 3000"));
方案2:用InversifyExpressUtils自动绑定路由
如果想简化路由配置,可使用inversify-express-utils提供的装饰器自动绑定:
改造ProductController.ts:
import { injectable, inject } from "inversify"; import { controller, httpGet } from "inversify-express-utils"; import { Request, Response } from "express"; import { ProductService } from "./ProductService"; import { TYPES } from "./types"; @injectable() @controller("/products") // 定义控制器的根路由 export class ProductController { private readonly productService: ProductService; constructor(@inject(TYPES.ProductService) productService: ProductService) { this.productService = productService; } @httpGet("/") // 对应 GET /products getProducts(req: Request, res: Response) { const products = this.productService.getAllProducts(); res.json(products); } @httpGet("/:id") // 对应 GET /products/:id getProductById(req: Request, res: Response) { const product = this.productService.getProductById(req.params.id); res.json(product); } }
然后在app.ts中初始化Inversify Express服务器:
import "reflect-metadata"; import { InversifyExpressServer } from "inversify-express-utils"; import { container } from "./container"; import express from "express"; // 必须导入控制器,让容器扫描到装饰器元数据 import "./ProductController"; const server = new InversifyExpressServer(container); // 配置Express中间件 server.setConfig((app) => { app.use(express.json()); }); const app = server.build(); app.listen(3000, () => console.log("Server running on port 3000"));
关键注意事项
- 所有需要被Inversify管理的类(服务、控制器)必须添加
@injectable()装饰器 - 确保反射元数据
reflect-metadata被正确导入,否则装饰器无法生效 - 容器绑定的生命周期(单例、瞬态等)可根据业务需求调整
内容的提问来源于stack exchange,提问作者Pankaj jha
相关产品推荐
相关产品推荐

