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 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加中间件,此时就是具体的路由的中间件了

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/876281.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Golang开发之路

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Go协程及并发锁应用指南

概念 协程&#xff08;Goroutine&#xff09;是Go语言独有的并发体&#xff0c;是一种轻量级的线程&#xff0c;也被称为用户态线程。相对于传统的多线程编程&#xff0c;协程的优点在于更加轻量级&#xff0c;占用系统资源更少&#xff0c;切换上下文的速度更快&#xff0c;不…

pyflink 安装和测试

FPY Warning! 安装 apache-Flink # pip install apache-Flink -i https://pypi.tuna.tsinghua.edu.cn/simple/ Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/ Collecting apache-FlinkDownloading https://pypi.tuna.tsinghua.edu.cn/packages/7f/a3/ad502…

【Docker部署ELK】(7.15)

1、拉取镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.15.0 docker pull docker.elastic.co/kibana/kibana:7.15.0 docker pull docker.elastic.co/logstash/logstash:7.15.02、配置文件&#xff08;解压资源到D盘DOCKER目录下&#xff09; 2.1 配置文件…

什么是java的spi?

Java SPI&#xff08;Service Provider Interface&#xff09;是一种提供服务发现机制的设计模式&#xff0c;允许在运行时动态地发现、加载和替换服务的实现。SPI机制的核心思想是&#xff1a;通过接口定义服务&#xff0c;并且使用外部的实现类来提供该服务的具体功能。 目录…

【delphi】判断多显示器下,程序在那个显示器中

在 Delphi 中&#xff0c;如果你的电脑连接了多个显示器&#xff0c;可以通过以下步骤判断某个程序在哪个显示器上运行。 方法概述&#xff1a; 获取程序窗口的位置&#xff08;例如窗体的 Left、Top 坐标&#xff09;。使用 Screen.MonitorFromWindow 函数来确定该窗口所属的…

【STM32】单级与串级PID控制的C语言实现

【STM32】单级与串级PID的C语言实现 前言PID理论什么是PIDPID计算过程PID计算公式Pout、Iout、Dout的作用单级PID与串级PID PID应用单级PID串级PID 前言 笔者最近在学习PID控制器&#xff0c;本文基于Blog做以总结。CSDN上已有大量PID理论知识的优秀文章&#xff0c;因此本文将…

短信验证码倒计时 (直接复制即可使用) vue3

需求&#xff1a; 要实现一个获取验证码的需求&#xff0c;点击获取验证码60秒内不可以重复点击&#xff0c;方式有两种可以直接复制使用&#xff1b; 效果图 实现方案 方案1 (单个文件内使用比较推荐) <el-button :disabled"codeDisabled" click.stop"h…

【在Linux世界中追寻伟大的One Piece】网络命令|验证UDP

目录 1 -> Ping命令 2 -> Netstat命令 3 -> Pidof命令 4 -> 验证UDP-Windows作为client访问Linux 4.1 -> UDP client样例 1 -> Ping命令 Ping命令是一种网络诊断工具&#xff0c;它使用ICMP(Internet Control Message Protocol&#xff0c;互联网控制消…

redis常见的数据类型?

参考&#xff1a;一文读懂Redis五种数据类型及应用场景 - 知乎 (zhihu.com) String 类型 String 类型&#xff1a;Redis 最基本的数据类型&#xff0c;它是二进制安全的&#xff0c;意味着你可以用它来存储任何类型的数据&#xff0c;如图片、序列化对象等。使用场景&#xff…

Qt入门教程---项目创建全过程内存泄漏解释

目录 1.创建项目的说明 2.代码介绍说明 2.1文件分类介绍 2.2sources文件 2.3widget.ui文件 2.4widget.h文件 2.5中间文件 2.6.pro文件 3.打印输出hello world 3.1图形化界面生成控件 3.2代码生成控件 3.3打印结果展示 4.对于内存泄露的讨论 4.1对象树 4.2与栈开辟…

一图读懂 若依后端

一图读懂 若依后端 关注我&#xff0c;评论区评论就能获得思维导图本体文件啦&#x1f604;。如果你愿意关注我的掘金就更好啦宝&#x1f60d;&#xff0c;因为我掘金文章就一内内人看&#xff0c;想引流&#x1f60b; https://juejin.cn/user/1942157160101860本图是对若依后…

基础GAN生成式对抗网络(pytorch实验)

&#xff08;Generative Adversarial Network&#xff09; 一、理论 https://zhuanlan.zhihu.com/p/307527293?utm_campaignshareopn&utm_mediumsocial&utm_psn1815884330188283904&utm_sourcewechat_session 大佬的文章中的“GEN的本质”部分 二、实验 1、数…

Java | Leetcode Java题解之第403题青蛙过河

题目&#xff1a; 题解&#xff1a; class Solution {public boolean canCross(int[] stones) {int n stones.length;boolean[][] dp new boolean[n][n];dp[0][0] true;for (int i 1; i < n; i) {if (stones[i] - stones[i - 1] > i) {return false;}}for (int i 1…

Oracle 11gR2打PSU补丁详细教程

1 说明 Oracle的PSU&#xff08;Patch Set Update&#xff09;补丁是Oracle公司为了其数据库产品定期发布的更新包&#xff0c;通常每季度发布一次。PSU包含了该季度内收集的一系列安全更新&#xff08;CPU&#xff1a;Critical Patch Update&#xff09;以及一些重要的错误修…

效率神器来了:AI工具手把手教你快速提升工作效能

随着科技的进步&#xff0c;AI工具已经成为提升工作效率的关键手段。本文将介绍一些实用的AI工具和方法&#xff0c;帮助你自动化繁琐的重复性任务、优化数据管理、促进团队协作与沟通&#xff0c;并提升决策质量。 背景&#xff1a;OOP AI-免费问答学习交流-GPT 自动化重复性任…

【Linux】【Vim】Vim 基础

Vim/Gvim 基础 文本编辑基础编辑操作符命令和位移改变文本重复改动Visual 模式移动文本(复制、粘贴)文本对象替换模式 光标移动以 word 为单位移动行首和行尾行内指定单字符移动到匹配的括号光标移动到指定行滚屏简单查找 /string标记 分屏vimdiff 文本编辑 基础编辑 Normal 模…

【网络安全的神秘世界】渗透测试基础

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 渗透测试基础 基于功能去进行漏洞挖掘 1、编辑器漏洞 1.1 编辑器漏洞介绍 一般企业搭建网站可能采用了通用模板&#xff…

app抓包 chrome://inspect/#devices

一、前言&#xff1a; 1.首先不支持flutter框架&#xff0c;可支持ionic、taro 2.初次需要翻墙 3.app为debug包&#xff0c;非release 二、具体步骤 1.谷歌浏览器地址&#xff1a;chrome://inspect/#devices qq浏览器地址&#xff1a;qqbrowser://inspect/#devi…

Lombok:Java开发者的代码简化神器【后端 17】

Lombok&#xff1a;Java开发者的代码简化神器 在Java开发中&#xff0c;我们经常需要编写大量的样板代码&#xff0c;如getter、setter、equals、hashCode、toString等方法。这些代码虽然基础且必要&#xff0c;但往往占据了大量开发时间&#xff0c;且容易在属性变更时引发错误…