由于Java转Go直接使用的goframe框架,然学习Gin时觉得一个接口一个路由太麻烦,于是有了...
1、在请求结构体中采用标签的形式,直接给出路由和请求方式
2、在控制层引用xxxReq开头的结构体作为入参(此时API结构体就与方法形成了联系)
3、通过反射获取控制层的所有方法,通过每个方法拿到结构体,并获取到标签信息,随后进行路由绑定
以上就是大致思路,中途也加入了路由分组、中间件设置等
反正也支持数据自动绑定到结构体中(Req),也能直接返回结构体数据(Res)等
label.go 文件
// Package common
// @Author 铁憨憨[cory] 2024/9/5 15:35:00
package common
type (
MetaCory = Meta // Meta is alias of frequently-used type gmeta.Meta.
)
type Meta struct{}
userApi.go 文件
// Package api
// @Author 铁憨憨[cory] 2024/9/5 15:23:00
package api
import (
"cory_chat_room/api/common"
"mime/multipart"
)
type UserReq struct {
common.MetaCory `path:"/getUserMenus" method:"get" tags:"用户管理" summary:"获取用户菜单"`
Name string `json:"name" form:"name" `
Age int `json:"age" form:"age" `
Flag bool `json:"flag" form:"flag" `
}
type UserRes struct {
Text string `json:"text" form:"text" `
}
type UserListReq struct {
common.MetaCory `path:"/getUserMenus" method:"post" tags:"用户管理" summary:"获取用户菜单"`
Name string `json:"name" form:"name" `
Age int `json:"age" form:"age" `
Flag bool `json:"flag" form:"flag" `
File *multipart.FileHeader `json:"file" form:"file" `
}
type UserListRes struct {
Text string `json:"text" form:"text" `
}
postController.go 文件
// Package controllers
// @Author 铁憨憨[cory] 2024/9/5 14:58:00
package controllers
import (
"cory_chat_room/api"
"fmt"
"github.com/gin-gonic/gin"
)
var Product = new(productController)
type productController struct {
}
func (uc *productController) GetProducts(c *gin.Context, req *api.UserReq) (res *api.UserRes, err error) {
a := new(api.UserRes)
a.Text = "风、风、大风"
// 处理获取用户的逻辑
fmt.Println("----", req.Name)
fmt.Println("----", req.Age)
fmt.Println("----", req.Flag)
return a, err
}
userController.go 文件
这块其实我想通过init调用方法来实现路由注册,但是有依赖注入就放弃了
// Package controllers
// @Author 铁憨憨[cory] 2024/9/6 16:58:00
package controllers
import (
"cory_chat_room/api"
"fmt"
"github.com/gin-gonic/gin"
)
var User = new(userController)
type userController struct {
}
//func init() {
// routers.TwoGroup(User)
//}
func (uc *userController) GetUserList(c *gin.Context, req *api.UserListReq) (res *api.UserListRes, err error) {
a := new(api.UserListRes)
a.Text = "人民有信仰,民族有希望,国家有力量。"
// 处理获取用户的逻辑
fmt.Println("----", req.Name)
fmt.Println("----", req.Age)
fmt.Println("----", req.Flag)
fmt.Println("----", req.File.Filename)
return a, err
}
package routers
import (
"github.com/gin-gonic/gin"
"reflect"
"strings"
)
// @Title controllerRoutingPacket
// @Description 根据控制器名称进行路由分组(也可单独为某一个路由设置中间件)
// @Author 铁憨憨[cory] 2024-09-06 16:29:19
// @Param userGroup 分组
// @Param controller 控制层
// @Param middlewares 中间件
func controllerRoutingPacket(userGroup *gin.RouterGroup, controller interface{}, middlewares ...gin.HandlerFunc) {
//如若没有分组,那么就以"/"做根分组
if userGroup == nil {
userGroup = allRouter.Group("/")
}
controllerName := lowerFirst(reflect.TypeOf(controller).Elem().Name()) //通过反射获取控制层名称
userGroup = userGroup.Group(controllerName)
AutoBindRoutes(userGroup, controller, middlewares...)
}
// @Title lowerFirst
// @Description 将字符串首字母小写,且带/
// @Author 铁憨憨[cory] 2024-09-06 16:18:57
// @Param str
// @Return string
func lowerFirst(str string) string {
if len(str) > 0 {
firstChar := strings.ToLower(string(str[0]))
return firstChar + str[1:]
}
return "/" + str
}
// @Title AutoBindRoutes
// @Description 自动路由绑定函数,支持传入路由分组和中间件
// @Author 铁憨憨[cory] 2024-09-06 16:20:44
// @Param group 分组
// @Param controller 控制层
// @Param middlewares 中间件
func AutoBindRoutes(group *gin.RouterGroup, controller interface{}, middlewares ...gin.HandlerFunc) {
ctrlType := reflect.TypeOf(controller)
ctrlValue := reflect.ValueOf(controller)
for i := 0; i < ctrlType.NumMethod(); i++ {
method := ctrlType.Method(i)
// 检查方法的参数数量
if method.Type.NumIn() != 3 {
continue // 必须有 3 个参数:*gin.Context, req
}
// 检查第一个参数是否是 *gin.Context
if method.Type.In(1) != reflect.TypeOf(&gin.Context{}) {
continue
}
// 检查第二个参数是否是以 "Req" 结尾的结构体指针
reqType := method.Type.In(2)
if reqType.Kind() != reflect.Ptr || !strings.HasSuffix(reqType.Elem().Name(), "Req") {
continue
}
// 提取路由信息
routePath, httpMethod := extractRouteInfo(reqType.Elem())
if routePath == "" || httpMethod == "" {
continue
}
// 注册路由到指定的分组,并追加中间件
group.Handle(httpMethod, routePath, append(middlewares, func(c *gin.Context) {
// 创建一个新的请求结构体实例
reqInstance := reflect.New(reqType.Elem()).Interface()
// 绑定请求数据,无论请求方法
if err := c.ShouldBind(reqInstance); err != nil {
c.JSON(400, gin.H{"error": "Invalid request", "details": err.Error()})
return
}
// 调用控制器方法
results := method.Func.Call([]reflect.Value{
ctrlValue, reflect.ValueOf(c), reflect.ValueOf(reqInstance),
})
// 处理返回值
if len(results) == 2 && results[1].IsNil() {
c.JSON(200, results[0].Interface())
} else {
c.JSON(500, gin.H{"error": "Internal server error"})
}
})...)
}
}
// @Title extractRouteInfo
// @Description 提取路由信息的辅助函数(目前主要提取路由和请求方式)
// @Author 铁憨憨[cory] 2024-09-06 16:21:06
// @Param reqType
// @Return string
// @Return string
func extractRouteInfo(reqType reflect.Type) (string, string) {
field := reqType.Field(0) // 假设路由信息总是在第一个字段中
path := field.Tag.Get("path")
method := field.Tag.Get("method")
return path, strings.ToUpper(method)
}
authMiddleware.go 中间件
// Package middlewares
// @Description: 中间件
package middlewares
import (
"fmt"
"github.com/gin-gonic/gin"
"time"
)
// LoggingMiddleware 记录请求路径和处理时间
func LoggingMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
startTime := time.Now()
// 继续执行其他中间件或处理请求
c.Next()
// 计算处理时间
duration := time.Since(startTime)
fmt.Printf("------------Request: %s, Duration: %v\n", c.Request.URL.Path, duration)
fmt.Printf("------------Request: %s, Duration: %v\n", c.Request.URL.Path, duration)
fmt.Printf("------------Request: %s, Duration: %v\n", c.Request.URL.Path, duration)
}
}
routingPacket.go 路由分组
// Package controllers
// @Author 铁憨憨[cory] 2024/9/6 15:35:00
package routers
import (
"cory_chat_room/controllers"
"cory_chat_room/middlewares"
"github.com/gin-gonic/gin"
)
var (
allRouter *gin.Engine //gin
)
func RouterInit() {
allRouter = gin.Default()
OneGroup("/api/v1")
TwoGroup("/api/v2")
err := allRouter.Run(":8080")
if err != nil {
return
}
}
func OneGroup(routerPath string) {
oneGroup := allRouter.Group(routerPath)
oneGroup.Use(middlewares.LoggingMiddleware())
controllerRoutingPacket(oneGroup, controllers.Product)
}
func TwoGroup(routerPath string) {
TwoGroup := allRouter.Group(routerPath)
controllerRoutingPacket(TwoGroup, controllers.User)
}
也可直接在controllerRoutingPacket加中间件,此时就是具体的路由的中间件了