处理程序闭包是一个不错的选择,但是当仅在该处理程序中使用参数时效果最好。
如果您有路由组或长处理程序链,在多个地方需要相同的参数,您应该将值设置到 Gin 上下文中。
您可以使用函数文字或返回的命名函数gin.HandlerFunc
以干净的方式执行此操作。
将配置注入路由器组的示例:
中间件包:
func Configs(conf APIV1Config) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("configKey", conf) // key could be an unexported struct to ensure uniqueness
}
}
路由器:
conf := APIV1Config{/* some api configs */}
// makes conf available to all routes in this group
g := r.Group("/api/v1", middleware.Configs(conf))
{
// ... routes that all need API V1 configs
}
这也很容易进行单元测试。假设您测试单个处理程序,您可以将必要的值设置到模拟上下文中:
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("configKey", /* mock configs */)
apiV1FooHandler(c)
现在,对于应用程序范围的依赖项(数据库连接、远程客户端等),我同意将这些直接设置到 Gin 上下文中是一个糟糕的解决方案。
然后你应该做的是,使用上面概述的模式将提供者注入到 Gin 上下文中:
中间件包:
// provider could be an interface for easy mocking
func DBProvider(provider database.Provider) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("providerKey", provider)
}
}
路由器:
dbProvider := /* init provider with db connection */
r.Use(DBProvider(dbProvider)) // global middleware
// or
g := r.Group("/users", DBProvider(dbProvider)) // users group only
处理程序(您可以通过将这些上下文获取器放在一些辅助函数中来大大减少样板代码):
// helper function
func GetDB(c *gin.Context) *sql.DB {
provider := c.MustGet("providerKey").(database.Provider)
return provider.GetConn()
}
func createUserHandler(c *gin.Context) {
db := GetDB(c) // same in all other handlers
// ...
}