当前位置: 首页 > news >正文

Gin-封装自动路由

O.0

  • 思路
  • 一、API
  • 二、控制层
  • 三、自动路由核心
  • 四、分组路由外加中间件使用

思路

由于Java转Go直接使用的goframe框架,然学习Gin时觉得一个接口一个路由太麻烦,于是有了...1、在请求结构体中采用标签的形式,直接给出路由和请求方式
2、在控制层引用xxxReq开头的结构体作为入参(此时API结构体就与方法形成了联系)
3、通过反射获取控制层的所有方法,通过每个方法拿到结构体,并获取到标签信息,随后进行路由绑定以上就是大致思路,中途也加入了路由分组、中间件设置等反正也支持数据自动绑定到结构体中(Req),也能直接返回结构体数据(Res)等

在这里插入图片描述

一、API

label.go 文件

// Package common
// @Author 铁憨憨[cory] 2024/9/5 15:35:00
package commontype (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 apiimport ("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 controllersimport ("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 controllersimport ("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 routersimport ("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.Contextif 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 middlewaresimport ("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 routersimport ("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加中间件,此时就是具体的路由的中间件了

在这里插入图片描述
在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • InstantMesh模型构建指南
  • Vue:通过js控制css变量 - 一键修改全局样式
  • 深度学习实践:理解理论背后的机制,体会理论与实际应用中的差距
  • 如何编写Prompt,利用AI高效生成图表——图表狐(FoxChart)指南
  • 设计模式-行为型模式-备忘录模式
  • Vue3如何查看项目是否安装了vue-router路由依赖,及安装方法
  • 数据结构与算法-17高级数据结构_图论(迪杰斯特拉算法)
  • 《黑神话:悟空》本地存档误删了怎么恢复?三种方法!
  • PHP7 json_encode() 浮点小数溢出错误
  • 攻防世界 ics-05
  • 【原创】java+swing+mysql项目管理系统设计与实现
  • OPENAIGC开发者大赛高校组银奖 | LonAC中小学编程学习平台
  • 借助MoAiStudio不写一行代码,完成页面开发
  • Redis的IO模型
  • 如何使用UWA Gears连接模拟器进行性能测试
  • 【mysql】环境安装、服务启动、密码设置
  • 77. Combinations
  • avalon2.2的VM生成过程
  • FineReport中如何实现自动滚屏效果
  • Golang-长连接-状态推送
  • Java程序员幽默爆笑锦集
  • nodejs实现webservice问题总结
  • python_bomb----数据类型总结
  • Tornado学习笔记(1)
  • vuex 笔记整理
  • windows下使用nginx调试简介
  • 第2章 网络文档
  • -- 数据结构 顺序表 --Java
  • 数据科学 第 3 章 11 字符串处理
  • 微信小程序开发问题汇总
  • 在Unity中实现一个简单的消息管理器
  • 函数计算新功能-----支持C#函数
  • 交换综合实验一
  • ‌分布式计算技术与复杂算法优化:‌现代数据处理的基石
  • #include
  • ( 10 )MySQL中的外键
  • (13):Silverlight 2 数据与通信之WebRequest
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (20050108)又读《平凡的世界》
  • (55)MOS管专题--->(10)MOS管的封装
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (day6) 319. 灯泡开关
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (六)c52学习之旅-独立按键
  • (七)Activiti-modeler中文支持
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .net 使用$.ajax实现从前台调用后台方法(包含静态方法和非静态方法调用)
  • .NET 使用配置文件
  • .net后端程序发布到nignx上,通过nginx访问
  • @staticmethod和@classmethod的作用与区别
  • @Transient注解
  • @vue-office/excel 解决移动端预览excel文件触发软键盘
  • []利用定点式具实现:文件读取,完成不同进制之间的