路由分离
上篇内容我们讲了该怎么定义路由和控制器,但是在实践中很显而易见的问题是,随着我们的路由数量增加,业务逻辑的复杂,把全部的路由放在 main 函数里,肯定会导致代码臃肿,且耦合度较高,维护起来比较复杂。所以我们需要进行路由的分离。
路由的分离听起来挺玄乎,其实就是将我们前面的路由代码抽离出来,单独放在一个文件中,并用函数对外暴露,然后再主函数中进行调用。从而实现在主函数中初始化路由,而真正的路由配置代码都在单独的文件中。
// router.go
func InitRouter(r *gin.Engine) {
r.GET("/hello", controller.HelloController)
}
// main.go
func main() {
r := gin.Default()
router.InitRouter(r)
r.Run()
}
路由分组
更进一步的,如果我们关于用户的操作有许多类似的 URL,比如/user/get,/user/add等,如果我们一个个的写不仅稍微有点麻烦,主要还是逻辑上不够合理。因为很显然,这些路由是具有相关性的,应当被当做一个路由组来进行处理。
func InitRouter(router *gin.Engine) {
rc := router.Group("/user")
{
rc.GET("/all", controller.UserList)
rc.POST("/action", controller.UserAction)
rc.GET("/follow", controller.FollowList)
rc.GET("/follower", controller.FollowerList)
rc.GET("/friend", controller.FriendList)
}
}
中间件
上次讲到在路由里可以传入控制器函数,对请求进行处理并返回数据。那么中间件就是在控制器进行处理前进行中间操作的函数,也就是我们认识的拦截器。
中间件和前面的控制器一样都是gin.HandlerFunc类型的函数,一般来讲我们也在单独的文件中定义以进行代码解耦合。
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authToken := c.DefaultQuery("token", "")
if authToken == "" {
authToken = c.PostForm("token")
}
// do something before
// next middleware or controller
c.Next()
// do something after
}
}
中间件我们可以用洋葱模型来理解。
外层就是我们写的 before 和 after 的逻辑,一层层的进来,再一层层的出去。
注意如果在中间件中使用 goroutine 的话,要将 context 的副本传入 goroutine,而不能直接传 context 的引用,可以用 c.Copy() 获取副本。
中间件可以是全局的,也可以某个路由的。而如果某个路由组都需要此中间件,也可以在定义路由组时直接使用。
r := gin.New()
// 全局中间件
r.Use(AuthMiddleware())
// 某个路由的中间件
func MiddlewareHandler1(c *gin.Engine) {
// do something before
c.Next()
// do something after
}
func MiddlewareHandler2(c *gin.Engine) {
// do something before
c.Next()
// do something after
}
// 可以使用多个中间件
r.GET("/hello", MiddlewareHandler1, MiddlewareHandler2, controller.HelloController)
// 路由组中间件
rc := r.Group("/user")
rc.Use(AuthMiddleware())
{
rc.POST("/login", controller.LoginController)
}