Gin-封装自动路由

devtools/2024/9/19 22:45:54/ 标签: 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加中间件,此时就是具体的路由的中间件了

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


http://www.ppmy.cn/devtools/112528.html

相关文章

C#使用TCP-S7协议读写西门子PLC(五)-测试程序

上面四篇我们进行封装连接PLC以及读写PLC内存地址表 C#使用TCP-S7协议读写西门子PLC(一)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(二)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(三)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(四)-CSDN博客 这里我们进行测试操作 西门子PLC-…

http://localhost:15672/ 无法访问

注意erlang版本和rabbitmq的版本要保持兼容 rabbitmq和erlang对应关系如下&#xff1a;https://www.rabbitmq.com/which-erlang.html 目前我选择的erlang版本是&#xff1a;otp_win64_24.3.3&#xff0c;rabbit版本是&#xff1a;rabbitmq-server-3.10.18.exe 如果两者之间的版…

基于ssm+vue+uniapp的新生报到系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

Docker安装mysql安装nginx安装Redis

Docker安装mysql 下载镜像 docker pull mysql:8.0注意,使用此方法安装镜像需要提前配置镜像源,详情看之前的文章 安装 docker run -d -p 3306:3306 \ --name mysql \ --restartalways \ --privilegedtrue \ -e TZAsia/Shanghai \ -e MYSQL_ROOT_PASSWORDroot \ mysql:8.0进…

Linux——应用层自定义协议与序列化

目录 一应用层 1再谈 "协议" 2序列化与反序列化 3理解read,write,recv,send 4Udp vs Tcp 二网络版本计算器 三手写序列和反序列化 四进程间关系与守护进程 1进程组 1.1什么是进程组 1.2组长进程 2会话 2.1什么是会话 2.2会话下的前后台进程 3作业控…

Redis集群_cluster

cluster集群 cluster翻译就是集群&#xff0c;所以cluster集群也叫做redis集群相比于哨兵模式&#xff0c;cluster集群能支持扩容&#xff0c;并且无需额外的节点来监控状态&#xff0c;所以使用这种模式集群的系统会用的更多些redis cluster采用的是去中心化网络拓扑架构&…

php返回数据量较大怎么办

当 PHP 返回数据量较大时&#xff0c;可能会导致内存溢出、超时、网络传输慢等问题。以下是一些常见的优化方案&#xff0c;帮助有效处理大数据量返回&#xff0c;确保系统的性能和稳定性&#xff1a; 1. 分页加载数据 问题描述&#xff1a;一次性返回大量数据&#xff08;例…

Safari-常用快捷键(IPadOS版本)

cmd1 切换数字对应的的标签页cmdL 跳转到地址栏cmdf 页内搜索cmdr 重新载入当前页(刷新)cmdd 当前页加入书签cmdctrl1/2/3 显示书签/阅读列表/浏览记录cmdt 新增标签页cmdw 关闭当前页(感谢csdn自动保存功能)cmd“/-” 放大/缩小页面cmdoptb 左侧打开书签并进入编辑模式cmdshif…

Leetcode 移动零

要求将数组中的所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。下面是该题的 C 解决方案&#xff1a; class Solution { public:void moveZeroes(vector<int>& nums) {int nonZeroPos 0; // 记录非零元素应该放置的位置// 遍历数组&#xff0c;…

【Android】【Compose】实现列表数据添加

序言 在使用列表的时候&#xff0c;以前是使用 Layout 布局里面添加Recyclerview进行列表的显示&#xff0c;但是在Compose里面&#xff0c;是没有这个Recyclerview使用的&#xff0c;那Compose怎么实现列表数据呢&#xff1f; 使用 【LazyColumn】 首先创建一个Compose项目…

如何用SQL Server和Oracle进行数据同步?

数据同步是大数据应用中非常重要的环节&#xff0c;它可以保证数据的实时性和一致性&#xff0c;为数据分析和决策提供重要依据。常见的数据同步方式包括ETL实时同步和实时ETL工具&#xff0c;其中实时ETL工具又可以分为基于日志追踪和基于触发器两种。 针对不同的数据库系统&…

网络丢包是如何产生的

在网络通信过程中&#xff0c;数据以数据包的形式在发送方和接收方之间传输。理想状态下&#xff0c;所有数据包都应准确无误地到达目的地。然而&#xff0c;在实际的网络环境中&#xff0c;数据包可能会在传输过程中丢失&#xff0c;这种现象被称为网络丢包。网络丢包会导致数…

【PostgreSQL】Npgsql.PostgresException:“3D000:

情景再现 原因 Npgsql.PostgresException 指出发生了一个 PostgreSQL 数据库的异常。错误代码 3D000 通常与 PostGIS 扩展有关&#xff0c;这是 PostgreSQL 中用于管理空间数据的扩展。错误信息 "SpatialDatabase2" 表明可能是在操作一个空间数据库对象时出现了问题…

【LeetCode】每日一题 2024_9_14 从字符串中移除星号(模拟)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 今天的题目曾经的我做过了 . . . 又是复习的一天 题目&#xff1a;从字符串中移除星号 代码与解题思路 func removeStars(s string) string {// 本题的核心&#xff1a;生成的输入保证总是可以执行题面中…

Acwing 区间合并

区间合并 主要思想&#xff1a;给定很多区间。若两个区间有交集&#xff0c;将二者合并成一个区间。 具体做法: 先按照区间的左端点进行排序然后遍历每个区间&#xff0c;根据不同的情况进行合并&#xff0c;有一下几种情况&#xff1a; 第一种情况&#xff0c;区间不变&…

Vue3.0组合式API:computed计算属性、watch监听器、watchEffect高级监听器

1、computed() 计算属性 在模板中绑定表达式只能用于简单的运算。如果运算比较复杂&#xff0c;可以使用 Vue.js 提供的计算属性&#xff0c;通过计算属性可以处理比较复杂的逻辑。 1.1 计算属性的应用 通过计算属性可以实现各种复杂的逻辑&#xff0c;包括运算、函数调用等…

Java小白一文讲清Java中集合相关的知识点(九)

Map和Set常用的API Map 常用API put(K key, V value): 将指定的键值对插入到映射中。get(Object key): 返回与指定键关联的值&#xff0c;如果键不存在则返回 null。remove(Object key): 移除指定键及其对应的值。containsKey(Object key): 判断是否包含指定的键。containsVa…

Python数据分析工具(一):Requests的用法

Python的Requests库是一个非常流行的HTTP库&#xff0c;用于发送各种HTTP请求。它简化了与Web服务的交互&#xff0c;提供了易于使用的API。以下是一些基本的Requests用法示例&#xff1a; 安装Requests 首先&#xff0c;确保你已经安装了Requests库。如果还没有安装&#xf…

Android 生成so库 并调用

第一步&#xff1a;创建so库 第二步&#xff1a;看一下Native的项目结构 自动生成引用文件build.gradle&#xff08;app&#xff09; externalNativeBuild {cmake {path file(src/main/cpp/CMakeLists.txt)version 3.22.1}} //每个版本的开发工具不一样 我这个是自动生成的so库…

AI Prompts Guide 【AI 提示语指南】

AI提示语浏览器插件&#xff1a;AIPRM for ChatGPT如何做一名Prompt Engineer 文章目录 开发音乐商务教育喜剧健康食物营销咖啡 开发 你是siri&#xff0c;一个由若干if-else短语组成的极其愚蠢的 “ai”。当我说 "嘿&#xff0c;Siri "时&#xff0c;你回答。请做一…