BeforeActivation + b.Dependencies().Add() 是控制器间共享数据库连接、日志实例等请求无关资源的唯一推荐方式,它实现自动注入、类型安全、无键冲突,避免 ctx.Values() 的类型断言错误和生命周期错配。

BeforeActivation注册依赖比ctx.Values()更安全
控制器之间共享数据库连接、日志实例、配置对象这类「请求无关但多控制器共用」的资源,BeforeActivation + b.Dependencies().Add() 是唯一推荐路径。它在控制器实例化时自动注入,类型安全、无键冲突、不依赖字符串 key,也避免了每次从 ctx.Values() 取值时的手动类型断言。
常见错误是把 ctx.Values().Set("db", db) 放进中间件,然后在多个 controller 里反复 ctx.Values().Get("db").(*sql.DB) ——这不仅冗余,还容易因 key 拼错或类型断言失败 panic。
-
b.Dependencies().Add(&sql.DB{})后,所有方法签名可直接声明func (c *UserController) Get(db *sql.DB) { ... } - 若需单例复用(如全局 Redis 客户端),
Add传指针即可;若需每次请求新建(如临时缓存实例),则在BeforeActivation中用闭包封装初始化逻辑 - 跨业务域复用同一服务(比如订单模块也要用用户校验),只需在
OrderController.BeforeActivation里再Add同一个AuthService实例,无需复制代码
不要用Context.Values()传服务层对象
ctx.Values() 设计初衷是传递「本次请求内临时数据」,比如解析后的用户 ID、JWT 载荷、请求追踪 ID。它底层是 sync.Map,高频写入有锁开销;更重要的是,它完全不解决生命周期问题——服务层对象(如 *sql.DB)本该长期存活,而 ctx 生命周期只有一瞬。
误用表现:启动后内存持续上涨、GC 频繁、偶发 panic: interface conversion: interface {} is nil。这是因为 ctx 复用时未清空 Values,旧值残留导致类型断言失败。
- 绝对不要在中间件里
ctx.Values().Set("logger", logger)再让 controller 去取——logger 本就是全局单例,直接Add更干净 - 若真要动态传参(如租户上下文),应走依赖注入参数,而非塞进
ctx.Values() -
ctx.Values()适合存int64、string、小结构体指针;大对象(如完整用户信息)建议只存 ID,后续按需查库
MVC控制器间共享状态必须绕过Context
Controller 实例本身不共享状态,Iris 的 MVC 默认每个请求新建 controller 实例(除非显式配置为 singleton)。所以「在 UserCtrl 里改了一个字段,期望 OrderCtrl 下次请求能读到」——这不可能发生。任何跨控制器的状态同步,都得走外部机制。
典型场景是「用户登录后,后续所有接口都要校验权限」。这不是 controller 间共享,而是「请求间状态延续」,必须用 session、JWT 或 Redis 存储会话标识,再在每个请求开始时解析并注入到当前 controller 依赖中。
- 登录成功后:
session := app.Sessions.Start(ctx); session.Set("user_id", 123) - 在
BeforeActivation中注入:b.Dependencies().Add(func(ctx iris.Context) *User { return loadUserFromSession(ctx) }) - 这样每个 controller 方法都能直接接收
*User参数,且保证是本次请求对应的用户
Singleton控制器慎用,优先走依赖注入
Iris 支持通过 mvc.Application.Register 注册 singleton controller,但这是反模式。singleton controller 会持有所有注入的依赖(包括 DB、Logger),一旦某个依赖被并发修改(比如 logger level 动态调整),就可能引发竞态;而且无法按请求隔离上下文(如 trace ID、租户 ID)。
真正需要「一份实例供全应用使用」的服务,应该注册为依赖项,而不是 controller 本身。controller 始终保持无状态,仅负责编排。
- 错误做法:
mvc.New(app).Register(new(UserService)).Handle(new(UserController)),然后在UserController里调c.UserService.Do() - 正确做法:
b.Dependencies().Add(new(UserService)),然后func (c *UserController) Get(svc *UserService) { svc.Do() } - singleton controller 唯一合理用途是管理全局定时任务或信号监听,与 HTTP 请求处理无关


















