使用gin封装一个web脚手架(四):改装中间件

·  阅读 137
使用gin封装一个web脚手架(四):改装中间件

上一篇文章中我们改装了路由组

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)

}
复制代码

运行浏览器,控制台打印

image.png

一般来说,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())
}
复制代码

访问浏览器

image.png

源码地址:github.com/PeterYangs/…

分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改