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

这篇具有很好参考价值的文章主要介绍了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 main

import (
	"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 routers

import (
	"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 routers

import (
	"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 routers

import (
	"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 web

import (
	"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 api

import (
	"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 admin

import (
	"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 admin

import (
	"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 admin

import (
	"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 main

import (
	"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 main

import (
	"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 main

import (
	"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 middlewares

import (
	"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 routers

import (
	"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 admin

import (
	"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)
}

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

注意事项文章来源地址https://www.toymoban.com/news/detail-832445.html

  • 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())

到了这里,关于Gin框架: 控制器, 中间件的分层设计案例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • gin中间件篇

    所有请求都经过此中间件 输出结果: 输出结果: 效果演示: 定义程序计时中间件,然后定义2个路由,执行函数后应该打印统计的执行时间,如下: 效果演示: 参考文章: https://www.fansimao.com/928855.html  gin Next()方法 - 范斯猫

    2024年01月21日
    浏览(38)
  • Go Gin中间件

    Gin是一个用Go语言编写的Web框架,它提供了一种简单的方式来创建HTTP路由和处理HTTP请求。中间件是Gin框架中的一个重要概念,它可以用来处理HTTP请求和响应,或者在处理请求之前和之后执行一些操作。 以下是关于Gin中间件开发的一些基本信息: 中间件的定义 :在Gin中,中

    2024年02月05日
    浏览(47)
  • GO——gin中间件和路由

    中间件 参考:https://learnku.com/articles/66234 结构 中间件是函数 中间件函数被放在调用链上 调用链的末尾是路由path对应的函数 执行过程 net/http包调用到gin的serverHTTP 参考:go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:506 通过path找到路由对应的处理链,赋值给context 参考:go/pkg/mod/git

    2024年01月17日
    浏览(41)
  • gin中使用限流中间件

    限流又称为流量控制(流控),通常是指限制到达系统的并发请求数,本文列举了常见的限流策略,并以gin框架为例演示了如何为项目添加限流组件。 限流又称为流量控制(流控),通常是指限制到达系统的并发请求数。 我们生活中也会经常遇到限流的场景,比如:某景区

    2024年01月25日
    浏览(43)
  • [架构之路-239]:目标系统 - 纵向分层 - 中间件middleware

    目录 前言: 一、中间件概述 1.1 中间件在软件层次中的位置 1.2 什么是中间件 1.3 为什么需要中间件 1.4 中间件应用场合(应用程序不用的底层需求:计算、存储、通信) 1.5 中间件分类 - 按内容分 二、嵌入式系统的中间件 2.1 概述 2.2 案例 三、分布式系统的中间件 3.1 概述

    2024年02月07日
    浏览(66)
  • go gin 全局中间件,以及设置值

    2024年02月11日
    浏览(37)
  • Golang中Gin 参数绑定和验证的中间件

    1. 学习在Golang中使用Gin参数绑定和验证的中间件,了解不同参数类型的绑定和验证方式。 Gin框架提供了很多常用的中间件,其中就包括参数绑定和验证的中间件。在使用Gin框架中进行数据绑定和验证时,可以使用Gin内置的Binding、Validating和Uri中间件。 1. Binding Binding中间件用于

    2024年02月08日
    浏览(49)
  • 简单记录下gin中使用中间件记录操作日志

    1、直接定义中间件 2、在需要使用的地方直接使用就可以,自动会收集日志到数据库中

    2024年02月09日
    浏览(46)
  • gin自定义中间件解决requestBody不可重复读问题

    先直接上代码 注意,上面的中间件,需要在第一个执行。 在gin中,context.Request.Body 是一个io.ReadCloser的接口,如下图 查看io.ReadCloser接口定义 我们发现io.ReaderCloser接口的本质就是 Read(p []byte) (n int, err error) 和 Close() error 的组合。 所以我们只需要自己编写实现 Read(p []byte) (n in

    2024年02月01日
    浏览(81)
  • Go学习第十七章——Gin中间件与路由

    Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等 即比如,如果访问一个网页的话,不管访问什么路径都需要进行登录,

    2024年02月07日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包