上一篇文章中我们改装了路由组
package routes
//routes/routes.go
import (
"github.com/gin-gonic/gin"
"myGin/controller"
)
func Load(r *gin.Engine) {
router := newRouter(r)
router.Group("/api", func(api group) {
api.Group("/user", func(user group) {
user.Registered(GET, "/info", controller.Index)
user.Registered(GET, "/order", controller.Index)
user.Registered(GET, "/money", controller.Index)
})
})
}
复制代码
但是这样写有一个问题,无法使用中间件了,我们需要完善一下,让其支持中间件并且中间件的context是由我们现实的context。
先在context中定义HandlerFunc
作为中间件函数
package context
//context/context.go
import (
"github.com/gin-gonic/gin"
"strings"
)
type Context struct {
*gin.Context
}
type HandlerFunc func(*Context)
func (c *Context) Domain() string {
return c.Request.Host[:strings.Index(c.Request.Host, ":")]
}
复制代码
再修改router.go,添加中间件参数
package routes
////routes/router.go
import (
"github.com/gin-gonic/gin"
"myGin/context"
"myGin/response"
)
type router struct {
engine *gin.Engine
}
type group struct {
engine *gin.Engine
path string
middlewares []context.HandlerFunc
}
type method int
const (
GET method = 0x000000
POST method = 0x000001
PUT method = 0x000002
DELETE method = 0x000003
ANY method = 0x000004
)
func newRouter(engine *gin.Engine) *router {
return &router{
engine: engine,
}
}
func (r *router) Group(path string, callback func(group), middlewares ...context.HandlerFunc) {
callback(group{
engine: r.engine,
path: path,
middlewares: middlewares,
})
}
func (g group) Group(path string, callback func(group), middlewares ...context.HandlerFunc) {
//需要注意,父级的中间件在前面
g.middlewares = append(g.middlewares, middlewares...)
g.path += path
callback(g)
}
func (g group) Registered(method method, url string, action func(*context.Context) *response.Response, middlewares ...context.HandlerFunc) {
//父类中间件+当前action中间件+action
var handlers = make([]gin.HandlerFunc, len(g.middlewares)+len(middlewares)+1)
//添加当前action的中间件
g.middlewares = append(g.middlewares, middlewares...)
//将中间件转换为gin.HandlerFunc
for key, middleware := range g.middlewares {
temp := middleware
handlers[key] = func(c *gin.Context) {
temp(&context.Context{Context: c})
}
}
//添加action
handlers[len(g.middlewares)] = convert(action)
finalUrl := g.path + url
switch method {
case GET:
g.engine.GET(finalUrl, handlers...)
case POST:
g.engine.GET(finalUrl, handlers...)
case PUT:
g.engine.PUT(finalUrl, handlers...)
case DELETE:
g.engine.DELETE(finalUrl, handlers...)
case ANY:
g.engine.Any(finalUrl, handlers...)
}
}
func convert(f func(*context.Context) *response.Response) gin.HandlerFunc {
return func(c *gin.Context) {
resp := f(&context.Context{Context: c})
data := resp.GetData()
switch item := data.(type) {
case string:
c.String(200, item)
case gin.H:
c.JSON(200, item)
}
}
}
复制代码
实现原理是通过callback不断的将父级的中间件累加,在Registered中计算所有中间件数量,转换完成后添加到gin的路由。
测试一下效果
在项目创建middleware目录并创建middleware.go
package middleware
//middleware/middleware.go
import (
"fmt"
"myGin/context"
)
func M1(c *context.Context) {
fmt.Println("我是1")
}
func M2(c *context.Context) {
fmt.Println("我是2")
}
func M3(c *context.Context) {
fmt.Println("我是3")
}
复制代码
创建了三个中间件,分别添加到不同层次的路由上
package routes
//routes/routes.go
import (
"github.com/gin-gonic/gin"
"myGin/controller"
"myGin/middleware"
)
func Load(r *gin.Engine) {
router := newRouter(r)
router.Group("/api", func(api group) {
api.Group("/user", func(user group) {
user.Registered(GET, "/info", controller.Index, middleware.M3)
user.Registered(GET, "/order", controller.Index)
user.Registered(GET, "/money", controller.Index)
}, middleware.M2)
}, middleware.M1)
}
复制代码
运行浏览器,控制台打印
一般来说,web框架会自带一些全局中间件,比如全局错误捕获中间件,我们继续来封装一下
先创建一个错误捕获中间件
package exception
import (
"fmt"
"myGin/context"
"runtime/debug"
)
func Exception(c *context.Context) {
defer func() {
if r := recover(); r != nil {
msg := fmt.Sprint(r) + "\n" + string(debug.Stack())
c.String(500, msg)
c.Abort()
}
}()
c.Next()
}
复制代码
再在项目下创建一个kernel并创建kernel.go文件
package kernel
import (
"myGin/context"
"myGin/middleware/exception"
)
// Middleware 全局中间件
var Middleware []context.HandlerFunc
func Load() {
Middleware = []context.HandlerFunc{
exception.Exception,
}
}
复制代码
在main.go中加载这个全局中间件
package main
import (
"github.com/gin-gonic/gin"
"myGin/kernel"
"myGin/routes"
)
func main() {
r := gin.Default()
//加载全局变量
kernel.Load()
routes.Load(r)
r.Run()
}
复制代码
在修改一下routes.go
package routes
//routes/routes.go
import (
"github.com/gin-gonic/gin"
"myGin/controller"
"myGin/kernel"
)
func config(router group) {
router.Group("/api", func(api group) {
api.Group("/user", func(user group) {
user.Registered(GET, "/info", controller.Index)
user.Registered(GET, "/order", controller.Index)
user.Registered(GET, "/money", controller.Index)
})
})
}
func Load(r *gin.Engine) {
router := newRouter(r)
router.Group("", func(g group) {
config(g)
}, kernel.Middleware...) //加载全局中间件
}
复制代码
主要思路是使用一个空路由,给这个空路由加载一个全局中间件
修改index控制器,模拟一个错误
package controller
//controller/Index.go
import (
"myGin/context"
"myGin/response"
)
func Index(context *context.Context) *response.Response {
panic("假装错误")
return response.Resp().String(context.Domain())
}
复制代码
访问浏览器