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"

type ApiCtrl struct{}

func (con ApiCtrl) Index(c *gin.Context) {
	c.String(200, "api接口总承")
}
func (con ApiCtrl) User(c *gin.Context) {
	c.String(200, "这是一个 user 接口")
}

5.3 admin/indexCtrl.go

package admin

import "github.com/gin-gonic/gin"

type IndexCtrl struct {}

func (con IndexCtrl) Index(c *gin.Context) {
	c.String(200, "admin 页面")
}

5.4 admin/baseCtrl.go

package admin

import "github.com/gin-gonic/gin"

type BaseCtrl struct{}

func (con BaseCtrl) success(c *gin.Context) {
	c.String(200, "成功")
}

func (con BaseCtrl) error(c *gin.Context) {
	c.String(200, "失败")
}

5.4 admin/userCtrl.go

package admin

import "github.com/gin-gonic/gin"

type UserCtrl struct {
	BaseCtrl
}

func (con UserCtrl) Index(c *gin.Context) {
	c.String(200, "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"
)

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(200, "首页")
	})

	r.GET("/news", initMiddleware, func(c *gin.Context) {
		c.String(200, "新闻页面")
	})

	r.Run()
}
  • 中间件就是匹配路由前和匹配路由完成后执行的一系列操作
  • 中间件必须是一个 gin.HandlerFunc 类型

2 )多个路由中间件

package main

import (
	"fmt"
	"time"
	"github.com/gin-gonic/gin"
)

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.GET("/", initMiddleware, initMiddleware2, func(c *gin.Context) {
		c.String(200, "首页")
	})

	r.GET("/news", initMiddleware, initMiddleware2, func(c *gin.Context) {
		c.String(200, "新闻页面")
	})

	r.Run()
}
  • 上述示例中,有两个中间件,就是 initMiddleware, initMiddleware2
  • 访问路由时的输出顺序
    第1个中间件开始
    第2个中间件开始
    第2个中间件结束
    第1个中间件结束,并统计其处理时间: 21000
    
  • 这种就是洋葱模型,基本上所有中间件都符合这一模型
  • 配置路由的时候可以传递多个 func 回调函数
  • 最后一个 func 回调函数前面触发的方法都可以称为中间件
  • 中间件里面加上 ctx.Next()可以让我们在路由匹配完成后执行一些操作

3 )全局中间件

package main

import (
	"fmt"
	"time"
	"github.com/gin-gonic/gin"
)

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(200, "首页")
	})

	r.GET("/news", func(c *gin.Context) {
		c.String(200, "新闻页面")
	})

	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 做数据统计

你可能感兴趣的:(Golang,gin,golang)