Gin框架: 控制器, 中间件的分层设计案例

对控制器的分组与继承


1 )设计项目目录结构

yourGinProject/ ·······························  根目录├── go.mod ··································  go mod 文件├── go.sum ··································  go sum 文件├── main.go ·································  main 文件└── tpls ····································· html模板目录│     └── web│     │    └── index.html├── routers ·································· 路由目录│     ├── webRouters.go│     ├── apiRouters.go│     └── adminRouters.go├── controllers ······························ 控制器目录│     ├── web│     │     └── webCtrl.go│     ├── api│     │     └── apiCtrl.go│     └── admin│     │     ├── base.go│     │     ├── indexCtrl.go│     │     └── userCtrl.go

2 )主程序 main.go

package mainimport ("gin-demo/routers" //gin-demo 是 go mod init 初始化的工程,下同"github.com/gin-gonic/gin"
)func main() {// 创建一个默认的路由引擎r := gin.Default()//加载模板 放在配置路由前面r.LoadHTMLGlob("tpls/**/*")routers.WebRoutersInit(r)routers.ApiRoutersInit(r)routers.AdminRoutersInit(r)r.Run()
}

3 ) HTML模板目录配置

tpls/web/index.html

{{ define "web/index.html" }}<h1>web index 页面</h1>{{.msg}}{{ end }}

4 ) routers 配置

4.1 webRouters.go

package routersimport ("gin-demo/controllers/web""github.com/gin-gonic/gin"
)func WebRoutersInit(r *gin.Engine) {webRouters := r.Group("/"){webRouters.GET("/", web.WebCtrl{}.Index)}
}

4.2 apiRouters.go

package routersimport ("gin-demo/controllers/api""github.com/gin-gonic/gin"
)func ApiRoutersInit(r *gin.Engine) {apiRouters := r.Group("/api"){apiRouters.GET("/", api.ApiCtrl{}.Index)apiRouters.GET("/user", api.ApiCtrl{}.User)}
}

4.2 adminRouters.go

package routersimport ("gin-demo/controllers/admin""github.com/gin-gonic/gin"
)func AdminRoutersInit(r *gin.Engine) {adminRouters := r.Group("/admin"){adminRouters.GET("/", admin.IndexCtrl{}.Index)adminRouters.GET("/user", admin.UserCtrl{}.Index)adminRouters.GET("/user/success", admin.UserCtrl{}.Success)adminRouters.GET("/user/error", admin.UserCtrl{}.Error)}
}

5 ) controller 配置

5.1 web/webCtrl.go

package webimport ("net/http""github.com/gin-gonic/gin"
)type WebCtrl struct{}func (con WebCtrl) Index(c *gin.Context) {c.HTML(http.StatusOK, "web/index.html", gin.H{"msg": "我是一个msg",})
}

5.2 api/apiCtrl.go

package apiimport ("github.com/gin-gonic/gin""net/http"
)type ApiCtrl struct{}func (con ApiCtrl) Index(c *gin.Context) {c.String(http.StatusOK, "api接口总承")
}
func (con ApiCtrl) User(c *gin.Context) {c.String(http.StatusOK, "这是一个 user 接口")
}

5.3 admin/indexCtrl.go

package adminimport ("github.com/gin-gonic/gin""net/http"
)type IndexCtrl struct {}func (con IndexCtrl) Index(c *gin.Context) {c.String(http.StatusOK, "admin 页面")
}

5.4 admin/baseCtrl.go

package adminimport ("github.com/gin-gonic/gin""net/http"
)type BaseCtrl struct{}func (con BaseCtrl) success(c *gin.Context) {c.String(http.StatusOK, "成功")
}func (con BaseCtrl) error(c *gin.Context) {c.String(http.StatusOK, "失败")
}

5.4 admin/userCtrl.go

package adminimport ("github.com/gin-gonic/gin""net/http"
)type UserCtrl struct {BaseCtrl
}func (con UserCtrl) Index(c *gin.Context) {c.String(http.StatusOK, "user 页面")
}
func (con UserCtrl) Success(c *gin.Context) {con.success(c)
}
func (con UserCtrl) Error(c *gin.Context) {con.error(c)
}

以上就是对控制器的一般文件拆分和继承关系的调用示例,验证如下

/ 访问首页
/api
/api/user
/admin
/admin/user
/admin/user/success
/admin/user/error

以上均可正常访问,这样就可以最快完成一个项目的拆分

中间件的处理


1 ) 基础用法, 单一中间件

package mainimport ("fmt""time""github.com/gin-gonic/gin""net/http"
)func initMiddleware(c *gin.Context) {// 记录开始时间start := time.Now().UnixNano()// 调用该请求的剩余处理程序c.Next()// 记录结束时间end := time.Now().UnixNano()// 输出当前渲染时间差fmt.Println("时间:", end - start)
}func main() {// 创建一个默认的路由引擎r := gin.Default()r.GET("/", initMiddleware, func(c *gin.Context) {c.String(http.StatusOK, "首页")})r.GET("/news", initMiddleware, func(c *gin.Context) {c.String(http.StatusOK, "新闻页面")})r.Run()
}
  • 中间件就是匹配路由前和匹配路由完成后执行的一系列操作
  • 中间件必须是一个 gin.HandlerFunc 类型

2 )多个路由中间件

package mainimport ("fmt""time""github.com/gin-gonic/gin""net/http"
)func initMiddleware(c *gin.Context) {fmt.Println("第1个中间件开始")// 记录开始时间start := time.Now().UnixNano()// 调用该请求的剩余处理程序c.Next()// 记录结束时间end := time.Now().UnixNano()// 输出当前渲染时间差fmt.Println("第1个中间件结束,并统计其处理时间:", end - start)
}func initMiddleware2(c *gin.Context) {fmt.Println("第2个中间件开始")c.Next()// 终止调用该请求的剩余处理程序// c.Abort() // 注意,Next 和 Abort 只能二选一,可以控制在某些情况下,终止中间件fmt.Println("第2个中间件结束")
}func main() {// 创建一个默认的路由引擎r := gin.Default()r.GET("/", initMiddleware, initMiddleware2, func(c *gin.Context) {c.String(http.StatusOK, "首页")})r.GET("/news", initMiddleware, initMiddleware2, func(c *gin.Context) {c.String(http.StatusOK, "新闻页面")})r.Run()
}
  • 上述示例中,有两个中间件,就是 initMiddleware, initMiddleware2
  • 访问路由时的输出顺序
    第1个中间件开始
    第2个中间件开始
    第2个中间件结束
    第1个中间件结束,并统计其处理时间: 21000
    
  • 这种就是洋葱模型,基本上所有中间件都符合这一模型
  • 配置路由的时候可以传递多个 func 回调函数
  • 最后一个 func 回调函数前面触发的方法都可以称为中间件
  • 中间件里面加上 ctx.Next()可以让我们在路由匹配完成后执行一些操作
  • 如果想要终止中间件操作可以通过判断,添加 ctx.Abort() 来终止接下来的操作

3 )全局中间件

package mainimport ("fmt""time""github.com/gin-gonic/gin""net/http"
)func initMiddleware(c *gin.Context) {fmt.Println("第1个中间件开始")// 记录开始时间start := time.Now().UnixNano()// 调用该请求的剩余处理程序c.Next()// 记录结束时间end := time.Now().UnixNano()// 输出当前渲染时间差fmt.Println("第1个中间件结束,并统计其处理时间:", end - start)
}func initMiddleware2(c *gin.Context) {fmt.Println("第2个中间件开始")c.Next()fmt.Println("第2个中间件结束")
}func main() {// 创建一个默认的路由引擎r := gin.Default()// 全局中间件r.Use(initMiddleware, initMiddleware2)r.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "首页")})r.GET("/news", func(c *gin.Context) {c.String(http.StatusOK, "新闻页面")})r.Run()
}
  • 这种属于全局配置的中间件,不用在每个路由中书写,进行全局use
  • 这种写法和第2种效果一致

4 )中间件的拆分

yourGinProject/ ·······························  根目录├── go.mod ··································  go mod 文件├── go.sum ··································  go sum 文件├── main.go ·································  main 文件└── tpls ····································· html模板目录│     └── web│     │    └── index.html├── routers ·································· 路由目录│     ├── webRouters.go│     ├── apiRouters.go│     └── adminRouters.go├── controllers ······························ 控制器目录│     ├── web│     │     └── webCtrl.go│     ├── api│     │     └── apiCtrl.go│     └── admin│     │     ├── base.go│     │     ├── indexCtrl.go│     │     └── userCtrl.go├── middlewares ······························ 中间件目录│     └── init.go

这里使用最顶层控制器拆分时用的结构

这里 middlewares/init.go

package middlewaresimport ("fmt""time""github.com/gin-gonic/gin"
)func InitMiddleware(c *gin.Context) {//判断用户是否登录fmt.Println("当前时间:", time.Now())fmt.Println("当前URL:", c.Request.URL)c.Set("username", "Wang") // 在请求上下文中设置值,后续的处理函数能够取到该值// 定义一个 goroutine 统计日志// 当在中间件或 handler 中启动新的 goroutine 时// 不能使用原始的上下文(c *gin.Context), 必须使用其只读副本(c.Copy())cCp := c.Copy()go func() {time.Sleep(2 * time.Second)fmt.Println("Done! in path " + cCp.Request.URL.Path)}()
}

改造 routers/adminRouters.go 文件

package routersimport ("gin-demo/controllers/admin""github.com/gin-gonic/gin""gin-demo/middlewares" // 引入
)func AdminRoutersInit(r *gin.Engine) {adminRouters := r.Group("/admin", middlewares.InitMiddleware) // 注意这里{adminRouters.GET("/", admin.IndexCtrl{}.Index)adminRouters.GET("/user", admin.UserCtrl{}.Index)adminRouters.GET("/user/success", admin.UserCtrl{}.Success)adminRouters.GET("/user/error", admin.UserCtrl{}.Error)}
}

/admin 及子路由被访问时都会经过这个中间件
这里用了一个 goroutine 做数据统计,下面在 admin.userCtrl 中获取中间件中配置的值

改造 controllers/admin/userCtrl.go 文件

package adminimport ("net/http""github.com/gin-gonic/gin"
)type UserCtrl struct {BaseCtrl
}func (con UserCtrl) Index(c *gin.Context) {username, _ := c.Get("username") // 这里从中间件中读取数据c.String(http.StatusOK, "user 页面: %v", username) // 响应出去
}
func (con UserCtrl) Success(c *gin.Context) {con.success(c)
}
func (con UserCtrl) Error(c *gin.Context) {con.error(c)
}

这样就可以获取到中间件中读取的数据了

注意事项

  • gin 默认中间件
    • gin.Default()默认使用了 Logger 和 Recovery 中间件,其中:
    • Logger 中间件将日志写入 gin.DefaultWriter,即使配置了 GIN_MODE=release
    • Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500 响应码
    • 如果不想使用上面两个默认的中间件,可以使用 gin.New() 新建一个没有任何默认中间件的路由
  • gin 中间件中使用 goroutine
    • 当在中间件或 handler 中启动新的 goroutine 时,不能使用原始的上下文(c *gin.Context)
    • 必须使用其只读副本(c.Copy())

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

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

相关文章

ping 8.8.8.8和ping www.baidu.com都OK,但是打不开网页

ping 8.8.8.8和ping www.baidu.com都OK&#xff0c;但是打不开网页 打开设置 -> 网络 找到IPV4, DNS栏输入 8.8.8.8 , apply 设置里界面变成这样 然后网页就能加载了

mysql(五) buffer pool(缓存页数据与索引数据)

前面写了很多mysql的实用基础知识、接下来我将总结整理一些进阶和更深的一些知识、你知道的越多、不知道就越多、让我们一起学习。 目录 一、buffer pool的位置&#xff08;Innodb存储引擎内&#xff09; 二、Buffer Pool是什么&#xff1f; 1、降低磁盘访问的机制 2、Buf…

揭秘阿里巴巴商品详情API:电商数据获取新篇章,业务效率飞跃提升

阿里巴巴平台商品详情API接口技术深度解析 一、概览 在阿里巴巴这个庞大的电商生态中&#xff0c;商品详情API接口&#xff08;item_get&#xff09;扮演着至关重要的角色。通过这一接口&#xff0c;开发者可以轻松地获取商品的详细信息&#xff0c;为选品、数据分析、价格监…

【机器学习算法】KNN鸢尾花种类预测案例和特征预处理。全md文档笔记(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论机器学习算法相关知识。机器学习算法文章笔记以算法、案例为驱动的学习&#xff0c;伴随浅显易懂的数学知识&#xff0c;让大家掌握机器学习常见算法原理&#xff0c;应用Scikit-learn实现机器学习算法的应用&#xff0…

Spark 离线开发框架设计与实现

一、背景 随着 Spark 以及其社区的不断发展&#xff0c;Spark 本身技术也在不断成熟&#xff0c;Spark 在技术架构和性能上的优势越来越明显&#xff0c;目前大多数公司在大数据处理中都倾向使用 Spark。Spark 支持多种语言的开发&#xff0c;如 Scala、Java、Sql、Python 等。…

读写锁学习笔记

1、数据结构 读锁是共享模式&#xff0c;写锁是独占模式&#xff0c;两个锁也公用一个AQS 两者共用一个state来表示&#xff0c;state前16位表示读锁&#xff0c;后16位表示写锁 读锁操作 通过向右位移16位&#xff0c;然后进行操作 写锁操作 通过和0000 0000 0000 0000 111…

【Spring连载】使用Spring Data访问 MongoDB(二)----Template API

【Spring连载】使用Spring Data访问 MongoDB&#xff08;二&#xff09;----Template API 一、方便的方法二、执行回调函数Execute Callbacks三、Fluent API四、异常转换五、域类型映射六、配置6.1 默认读取首选项Read Preference6.2 WriteResultChecking策略6.3 默认写安全Wri…

同一台宿主机上虚拟机CPU资源分配方式介绍

同一台物理机上运行多个虚拟机时&#xff0c;虚拟机之间的 CPU 资源分配方式通常有以下几种&#xff1a; 份额&#xff08;Share&#xff09;&#xff1a;为每个虚拟机分配一定的 CPU 份额&#xff0c;表示该虚拟机在 CPU 时间片分配中所占的比例。份额越高&#xff0c;虚拟机获…

VR全景在文旅景区方面的应用有哪些?

文旅是一个大消费行业&#xff0c;核心是面对C端客户&#xff0c;因此用户的体验非常重要。在这个基础上文旅景区是希望将风景传播给更多人&#xff0c;因此传播和裂变也是文旅行业所需的&#xff0c;而VR全景技术正好可以满足文旅景区所有的价值要素。 VR全景在文旅景区方面的…

预训练大模型LLM的微调PEFT(概述)

前言 大模型现在已经不是什么新奇玩意了&#xff0c;大家都耳熟能详&#xff0c;我们都知道大模型要从头到尾的训练&#xff0c;需要巨大的人力物力&#xff0c;如此高昂的费用小公司是难以承受的&#xff0c;那么我们是否可以像牛顿一样&#xff0c;站在巨人的肩膀上&#xf…

2000-2022年上市银行相关指标数据

2000-2022年上市银行相关指标数据 1、时间&#xff1a;2000-2022年 2、来源&#xff1a;附在文件内&#xff0c;见表格名称 3、指标&#xff1a;证券代码、证券简称、上市日期、上市公司(银行)类型、不良贷款余额、不良贷款拨备覆盖率、不良贷款比率、净利差、净息差、净息差…

el-button 选择与非选择按钮批量处理

el-button 选择与非选择按钮批量处理 <el-button v-for"(voyage,i) in data[voyages][nowVoyage]":key"i"class"c-work-bts"type"primary":plain"nowWorkSpace!i"click"chooseWorkSpace(i)"size"small&qu…

判断一个dll/exe是32位还是64位

通过记事本判断&#xff08;可判断C或者C#&#xff09; 64位、将dll用记事本打开&#xff0c;可以看到一堆乱码&#xff0c;但是找到乱码行的第一个PE&#xff0c;如果后面是d?则为64位 32位、将dll用记事本打开&#xff0c;可以看到一堆乱码&#xff0c;但是找到乱码行的第…

龙讯旷腾PWmat用户福利,DFTB紧束缚法上线!

DFTB介绍 DFTB是基于密度泛函理论的紧束缚方法&#xff08;Density Functional based Tight binding method&#xff09;。其融合了DFT的准确性和紧束缚方法(TB)的高效性&#xff0c;是一种近似DFT的方法。通过使用预先计算的参数、最小基组、仅考虑最近邻相互作用&#xff0c…

Prometheus+influxdb1.8实现高可用监控系统

背景 Prometheus是业内有名的开源监控工具&#xff0c;我所在的公司也是采用PrometheusGrafana方式构建监控系统&#xff0c;并且不只是监控运维层面的数据&#xff0c;业务层面的服务状态也通过Java代码的客户端micrometer向Prometheus提交数据并在Grafana上配置出图&#xf…

[小记]shell获取git最近一次提交信息

‘%H’: commit hash ‘%h’: abbreviated commit hash ‘%T’: tree hash ‘%t’: abbreviated tree hash ‘%P’: parent hashes ‘%p’: abbreviated parent hashes ‘%an’: author name ‘%aN’: author name (respecting .mailmap, see git-shortlog[1] or git-bl…

day2:信号与槽

思维导图 使用手动连接&#xff0c;将登录框中的取消按钮使用t4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断u界面上输入的账号是否为"123",密码是否为"…

【2024软件测试面试必会技能】allure测试报告(3): allure测试报告的用例描述设置

allure测试报告的用例描述相关方法&#xff1b;如下图 allure标记用例级别severity 在做自动化测试的过程中&#xff0c;测试用例越来越多的时候&#xff0c;如果执行一轮测试发现了几个测试不通过&#xff0c;我们也希望能快速统计出缺陷的等级。 pytest结合allure框架可以对…

R语言【base】——data.frame():创建数据框,紧耦合的变量集合,它们共享矩阵和列表的许多属性,被大多数R建模软件用作基本数据结构。

Package base version 4.2.0 Description 创建数据框&#xff08;data frame&#xff09;&#xff0c;紧耦合的变量集合&#xff0c;它们共享矩阵和列表的许多属性&#xff0c;被大多数R建模软件用作基本数据结构。 数据框&#xff1a;一种在统计分析和数据处理中常用的数据结…

二叉搜索树(二叉排序树、二叉查找树)

二叉搜索树&#xff08;二叉排序树、二叉查找树&#xff09; 一、定义二、操作&#xff08;一&#xff09;中序遍历&#xff08;二&#xff09;查找&#xff08;三&#xff09;插入&#xff08;四&#xff09;删除 三、二叉搜索树的应用四、二叉搜索树操作的性能分析五、总结 一…