不能直接在main.go写所有路由,因为iris路由方法需在应用实例初始化后调用,模块化路由可避免多实例、中间件不共享、生命周期错乱等问题;应通过func(app *iris.application)形式封装路由注册,主程序统一调用并确保中间件优先注册、静态资源路由置后。

为什么不能直接在 main.go 里写所有路由
因为 Iris 的 app.Handle、app.Get 等方法必须在应用实例初始化后调用,而模块化路由本质是把路由注册逻辑拆到独立文件中,再由主程序统一挂载。如果直接在子包里 new 一个 iris.Application,会导致多个独立服务实例、中间件不共享、生命周期错乱——常见表现为中间件不生效、ctx.Values() 丢失、静态资源 404。
如何定义可复用的路由模块(RouterGroup)
用函数封装路由注册行为,接收 *iris.Application 或 iris.Party 作为参数,避免自行创建 app 实例。推荐以 func(app *iris.Application) 形式导出,便于主程序统一调用:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
// routers/user.go
package routers
import "github.com/kataras/iris/v12"
func SetupUserRoutes(app *iris.Application) {
userAPI := app.Party("/api/v1/users")
{
userAPI.Use(verifyTokenMiddleware) // 可复用中间件
userAPI.Get("/", listUsers)
userAPI.Post("/", createUser)
userAPI.Get("/{id:uint64}", getUserByID)
}
}
- 不要返回
iris.Party,主程序不需要接管子路由对象 - 中间件应定义在模块内或单独 middleware 包,避免闭包捕获外部变量引发并发问题
- 路径参数类型如
{id:uint64}要显式声明,否则ctx.Params().GetUint64("id")会 panic
如何在 main.go 中正确加载多个路由模块
按顺序调用各模块的 setup 函数即可,Iris 路由树是追加式构建,无覆盖风险。注意:中间件注册(UseGlobal / Use)需在任何路由注册前完成:
// main.go
func main() {
app := iris.New()
// 全局中间件(必须放最前)
app.Use(recoverHandler, loggerHandler)
// 模块化加载
routers.SetupUserRoutes(app)
routers.SetupProductRoutes(app)
routers.SetupAdminRoutes(app)
// 静态资源要放在最后,避免被前面的通配路由拦截
app.HandleDir("/static", "./public")
app.Listen(":8080")
}
- 模块间路由冲突(如都注册了
/health)不会报错,后注册的会覆盖前一个——需靠团队规范或 CI 检查路径唯一性 - 若某模块需专属前缀(如 admin 后台),应在模块内部用
app.Party("/admin"),而非让 main.go 传入Party对象,否则难以测试和复用 -
app.HandleDir和app.Favicon也属于路由注册,务必放在所有业务路由之后
为什么 iris.Party 不适合跨包传递
iris.Party 是轻量上下文对象,本身不持有路由树,它只是对当前 *Application 的临时路径代理。一旦在子包中保存 Party 并延迟调用其 Get 等方法,可能因主程序执行顺序不同导致挂载位置错误——比如本该挂在 /api 下的路由,实际注册到了根路径。
- 绝对不要在 router 包里定义全局
var UserRouter = app.Party(...) - 也不要在模块函数里 return
Party,然后在 main.go 里继续链式调用;这破坏了模块职责,且无法统一应用中间件 - 真正需要隔离配置时(如不同子域名),应使用
app.Subdomain或app.Otherwise,并在主流程中集中控制










