Gin-封装自动路由

news2024/11/17 19:40:10

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

yum源配置与静态配置地址

网络yum源 备份配置文件 下载新的CentOS-Base.repo文件到/etc/yum.repos.d/目录下 执行yum clean all清除原有 yum 缓存 执行yum makecache&#xff08;刷新缓存&#xff09; 本地yum 将/etc/yum/repos.d/下的文件a都移走&#xff0c;此处移到了该目录下的bak中 找到光盘路…

【重学 MySQL】二十二、limit 实现分页

【重学 MySQL】二十二、limit 实现分页 基本语法实现分页第一页第二页通用公式注意事项在 MySQL 中,LIMIT 子句非常强大,它允许你限制查询结果的数量,同时也经常被用来实现分页功能。分页是 Web 开发中常见的需求,它允许用户浏览大量数据时,一次只查看一小部分数据。 基本…

【重学 MySQL】二十一、order by 实现数据排序

【重学 MySQL】二十一、order by 实现数据排序 基本语法示例按薪水升序排序按薪水降序排序根据多个列排序 注意事项 在MySQL中&#xff0c;ORDER BY子句用于对结果集中的数据进行排序。你可以根据一个或多个列对结果进行升序&#xff08;ASC&#xff09;或降序&#xff08;DESC…

JavaEE:文件操作

文章目录 文件操作和IO文件系统操作File介绍属性构造方法方法 代码演示前四个listmkdirrenameTo 文件操作和IO 文件系统操作 创建文件,删除文件,创建目录,重命名… Java中有一个类,可以帮我们完成上述操作. 这个类叫做File类. File介绍 属性 这个表格描述了文件路径的分隔符…

【IIS实战】ERR_SSL_KEY_USAGE_INCOMPATIBLE

当我们第一次配置IIS服务器做测试环境网站时&#xff0c;如果没有插手做自签名证书&#xff0c;而是用IIS自带的自签名证书&#xff0c;那么现代浏览器访问HTTPS测试站点大概率会有下图所示的报错&#xff1a; &#xff08;IE&#xff1a;我能打开( •̀ ω •́ )y&#xff0…

VuePress搭建个人博客(手动安装)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

ENSP配置云服务找不到以太网卡【已解决】

在搭建网络拓扑图的时候&#xff0c;想要连接云&#xff0c;发现没有以太网卡 环境&#xff1a;Windows10&#xff0c;ensp模拟器 以为一直是用轻薄本&#xff0c;上网都是连接wifi&#xff0c;所以没用上以太网卡。 一、在电脑环境上安装以太网卡 winR跳出运行口&#xff0c…

chapter13-常用类——(StringBuffer StringBuilder)—day15

475-StringBuffer结构剖析 476-StringBuffer转换 477-StringBuffer方法

2024.9.9

优化登录框&#xff1a; 当用户点击取消按钮&#xff0c;弹出问题对话框&#xff0c;询问是否要确定退出登录&#xff0c;并提供两个按钮&#xff0c;yes|No&#xff0c;如果用户点击的Yes&#xff0c;则关闭对话框&#xff0c;如果用户点击的No&#xff0c;则继续登录 当用户…

Java后台生成二维码

一、效果图 二、实现代码 1.添加依赖 <!-- zxing生成二维码 --> <dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version> </dependency><dependency><grou…

【Dart 教程系列第 50 篇】在 Flutter 项目的国际化多语言中,如何根据翻译提供的多语言文档表格,快速生成不同语言的内容

这是【Dart 教程系列第 50 篇】&#xff0c;如果觉得有用的话&#xff0c;欢迎关注专栏。 博文当前所用 Flutter SDK&#xff1a;3.22.1、Dart SDK&#xff1a;3.4.1 文章目录 一&#xff1a;问题描述二&#xff1a;解决方案三&#xff1a;完整代码 一&#xff1a;问题描述 在…

学会分析问题,画出分析图,解释问题过程,找出规律 ;整数数组分为左右2个部分,左边位奇数右边偶数

// 整数数组左边是奇数右边是偶数.cpp : Defines the entry point for the console application. //#include "stdafx.h" #include<stdio.h> void swap(int& a,int& b) {int tempa;ab;btemp; } int main(int argc, char* argv[]) {int a[7]{1,2,3,4,5,…

使用jenkins 打包前端私服代码失败的问题

问题现象&#xff1a; jinekins 流水线在yarn 编译前端私服依赖包的时候&#xff0c;报错&#xff0c;提示 Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password). 【emm。。。之前的构建都是好好的&#xff0c;也不知道前端大哥啥时候去封装的前端代码&am…

【每日刷题】Day115

【每日刷题】Day115 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. LCR 089. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 2. LCR 090. 打家劫舍 II - 力扣&…

阿里云服务器镜像,有大用处

大家好&#xff0c;我是小悟 有时候阿里云旧服务器快到期了&#xff0c;想把项目、数据、软件挪到新服务器上&#xff0c;如果全部重新搭建的话&#xff0c;那无疑是耗时又费力。有了镜像迁移&#xff0c;就方便了许多。 新旧服务器的类型要一致&#xff0c;比如都是ECS服务器…

Matlab程序练习

Part1 1.求 [100,999] 之间能被 21整除的数的个数。 程序&#xff1a; 主文件&#xff1a;main.m clear; start_num 100; end_num 999; div_num 21; res div(start_num,end_num,div_num); fprintf("[%d,%d]之间能被%d整除的数的个数为%d个\n",start_num,end_…

使用Azure+C#+visual studio开发图像目标检测系统

在这篇文章里面&#xff0c;我们讲解使用AzureC#visual studio在Azure上做图像的目标检测系统。 笔者是头一次接触C#。之前以Python Java和Scala为主。感觉C#.Net是一种挺好用的开发系统。C#和Java非常像。会一个学另一个很快。 首先&#xff0c;目标检测是个什么东西&#x…

vulhub spring 远程命令执行漏洞(CVE-2022-22963)

1.执行以下命令启动靶场环境并在浏览器访问 cd spring/CVE-2022-22963docker-compose up -ddocker ps 2.反弹shell 构造payload 3.页面刷新抓包&#xff0c;修改内容 POST /functionRouter HTTP/1.1 Host: 172.16.1.89:8080 Accept-Encoding: gzip, deflate Accept: */* Acc…

任务栏颜色怎么改?快速实现Windows系统任务栏透明,全面指南和操作实践!

任务栏是电脑操作系统的重要组成部分&#xff0c;电脑会自动为任务栏选择颜色&#xff0c;一般分为浅色&#xff08;白色&#xff09;、深色&#xff08;黑色&#xff09;。如果想要设置成自己喜欢的颜色&#xff0c;也是可以更改的&#xff0c;那么任务栏颜色怎么改呢&#xf…

实时图像处理的加速器:《基于FPGA的数字图像处理原理及应用》(可下载)

图像处理技术已成为我们生活中不可或缺的一部分。从智能手机的摄像头到卫星图像分析&#xff0c;从医疗影像到安全监控&#xff0c;图像处理技术的应用无处不在。随着技术的进步&#xff0c;我们对图像处理的速度和质量要求也越来越高。在这一背景下&#xff0c;现场可编程门阵…