Go gin 框架整理

目前做web api项目的语言主要有php、java、python以及go等,每种语言都有很多框架来方便我们快速构建项目,这就是框架存在的意义,有了框架,我们就不必每个项目都重写很多不必要的代码,只需要专注业务逻辑,有人说有了框架,接下来程序员只需要面向过程写代码就可以了,这话一定程度上是对,但是,如果要考虑系统业务代码的扩展性、复用性以及可维护性,在实际编码的过程中,还是要坚持OOP编程思想,及时冗余代码,以免积重难返。

1 web框架梳理

languageframework
PHPlaravel,yii,lumen,ci、zend、symfony
JavaSSH, Springboot
pythondjongo、flask
gogin、beego、echo、Iris

这是目前比较常见的框架,java相关的没接触过,其他的都有接触,其实本质上都是一样的,mvc,组件化等等,如果深入研究透彻一款,其他的只是语言实现的区别。

2 包管理工具

这是现代编程语言必备的工具,要不然管理各种依赖包及其版本,肯定是灾难。

languagepackage tools
jsnpm
javamaven
phpcomposer
pythonpip3
rubyrubygem
gogo.mod (开启GO111MODULE)

3 Gin框架

3.1 初始化test项目
go mod init test

//go.mod文件
module test

go 1.15
3.2 install gin framework
go get 

//go.mod 文件
module test

go 1.15

require (
	github.com/gin-gonic/gin v1.6.3 // indirect
	github.com/go-playground/validator/v10 v10.4.1 // indirect
	github.com/golang/protobuf v1.4.3 // indirect
	github.com/json-iterator/go v1.1.10 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.1 // indirect
	github.com/ugorji/go v1.1.13 // indirect
	golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
	golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd // indirect
	google.golang.org/protobuf v1.25.0 // indirect
	gopkg.in/yaml.v2 v2.3.0 // indirect
)

从go.mod文件中看到gin 1.6.3版本以及其他核心库信息,同时生成了go.sum文件,其记录每个依赖库的版本和哈希值,目的在于分布式包管理的一致性和安全性。

3.3 创建服务并启动
package main

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

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
            c.String(http.StatusOK, "hello word")
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

//启动服务
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

3.4 参数接收
方法获取
GETc.DefaultQuery(“name”, “Guest”) 、c.Query(“name”)
POSTname := c.PostForm(“name”)
Filefile, _ := c.FormFile(“file”)
3.5 定义路由及路由分组
router := gin.Default()
router.get("/ping", func(c *gin.Context) {})
v1 := r.Group("/v1")
{
    v1.GET("/getuser", func(c *gin.Context) {
 		c.String(http.StatusOK, "jack")
	})
}
//访问方式
http://localhost:8080/v1/getuser
//返回
jack

3.6 中间件

中间件技术能有效解决很多问题,想想laravel框架的中间件就知道,比如token验证、返回值格式化等

gin默认启动方式下,包含Logger、Recovery中间件

func Default() *Engine {  
   engine := New()  
   engine.Use(Logger(), Recovery())  
   return engine  
}

//定义一个在请求响应header中写入请求id的中间件
func RequestIdMiddleware() gin.HandlerFunc {
  return func(c *gin.Context) {
    c.Writer.Header().Set("X-Request-GinId", uuid.NewV4().String())
    c.Next()
  }
}

//使用
r := gin.Default()
r.Use(RequestIdMiddleware())

//在请求头里就会有X-Request-GinId项
X-Request-Ginid: c5d2d11e-891b-4299-8021-cb9298129b3f
3.7 日志收集

使用gin.DefaultWriter 或者 logrus日志库,当然你可以自定义日志格式,满足业务需求

logfile, err := os.Create("./gin_http.log")
if err != nil {
 fmt.Println("Could not create log file")
}
gin.SetMode(gin.DebugMode)
gin.DefaultWriter = io.MultiWriter(logfile)

//log file
[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] GET    /v1/getuser               --> main.main.func2 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

如果使用logrus日志库,请参考相关文档【logrus 日志库】,使用很方便,简捷。

3.8 代码即时生效工具Air

一般的,这种工具都是开发时使用,例如beego里的bee,gin的air等

go get -u github.com/cosmtrek/air

在项目目录touch .air.conf,并写入以下参考配置文件

# [Air](https://github.com/cosmtrek/air) TOML 格式的配置文件

# 工作目录
# 使用 . 或绝对路径,请注意 `tmp_dir` 目录必须在 `root` 目录下
root = "."
tmp_dir = "tmp"

[build]
# 只需要写你平常编译使用的shell命令。你也可以使用 `make`
cmd = "go build -o ./tmp/main.exe ."
# 由`cmd`命令得到的二进制文件名
bin = "tmp/main.exe"
# 自定义的二进制,可以添加额外的编译标识例如添加 GIN_MODE=release
full_bin = "SET APP_ENV=dev & SET APP_USER=air & .\tmp\main.exe"
# 监听以下文件扩展名的文件.
include_ext = ["go", "tpl", "tmpl", "html"]
# 忽略这些文件扩展名或目录
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
# 监听以下指定目录的文件
include_dir = []
# 排除以下文件
exclude_file = []
# 如果文件更改过于频繁,则没有必要在每次更改时都触发构建。可以设置触发构建的延迟时间
delay = 1000 # ms
# 发生构建错误时,停止运行旧的二进制文件。
stop_on_error = true
# air的日志文件名,该日志文件放置在你的`tmp_dir`中
log = "air_errors.log"

[log]
# 显示日志时间
time = true

[color]
# 自定义每个部分显示的颜色。如果找不到颜色,使用原始的应用程序日志。
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"

[misc]
# 退出时删除tmp目录
clean_on_exit = true

利用air命令启动项目,后续代码的修改,会自动编译生效。

3.9 模型绑定校验

gin/binding 内置模型绑定实现,将请求数据提取到合适的绑定器,Gin 主要提供了两组绑定方法 Must bind 与 Should bind 。前者使用框架自带的处理机制,基本上验证不通过,就会被终止或抛出特定的错误页面。后者存在绑定错误,这个错误会被返回, 需要开发者去处理相应的请求和错误,具备灵活性。两者都提供了Bind, BindJSON, BindXML, BindQuery, BindYAML等绑定方法,可以根据场景判定使用。

type Person struct {
	Name    string `form:"name"`
	Address string `form:"address"`
}

//endpoint中,绑定query参数
var person Person
	if c.BindQuery(&person) == nil {
}
3.10 模板渲染

新建templates目录,并新建index.html文件

<html>
<h1>
	{{.title}}
</h1>
</html>
//define router
r.GET("/index", func(c *gin.Context) {
	c.HTML(http.StatusOK, "index.tmpl", gin.H{
		"title": "Main website",
	})
})

http://localhost:8080/index
即可显示模板信息 Main website

现代的模板都是公共的header,footer,中间嵌套不同页面的不同内容,分目录组织模板文件是绝对必要的,当然现在基本上不用从头写了,直接找现成的模板改造使用。

3.11 多服务启动
package main

import (
	"log"
	"net/http"
	"time"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
)

var (
	g errgroup.Group
)

func router01() http.Handler {
	r1 := gin.New()
	r1.Use(gin.Recovery())
	r1.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 01",
			},
		)
	})
	return r1
}

func router02() http.Handler {
	r2 := gin.New()
	r2.Use(gin.Recovery())
	r2.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 02",
			},
		)
	})
	return r2
}

func main() {
	server01 := &http.Server{
		Addr:         ":8080",
		Handler:      router01(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	server02 := &http.Server{
		Addr:         ":8081",
		Handler:      router02(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	g.Go(func() error {
		return server01.ListenAndServe()
	})

	g.Go(func() error {
		return server02.ListenAndServe()
	})

	if err := g.Wait(); err != nil {
		log.Fatal(err)
	}
}
3.12 测试

包net/http/httptest是进行api接口测试首选

w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
router.ServeHTTP(w, req)

assert.Equal(t, 200, w.Code)
assert.Equal(t, "pong", w.Body.String())
3.13 服务的不停服启停
router := gin.Default()
router.GET("/", handler)
// [...]
endless.ListenAndServe(":4242", router)

或者
manners:一个Go HTTP服务器,能优雅的关闭
graceful:Graceful是一个go的包,支持优雅地关闭http.Handler服务器
grace:对Go服务器进行优雅的重启和零停机部署

参考文档
1 go mod
2 go 基本知识点总结
3 go 中文文档

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值