6

Go Gin 简明教程

 3 years ago
source link: https://geektutu.com/post/quick-go-gin.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Go Gin 简明教程

Gin

关键字:Gin教程 Gin中文文档 Go语言Web框架 Go环境搭建

Gin 简介

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance – up to 40 times faster. If you need smashing performance, get yourself some Gin.

Gin 是使用 Go/golang 语言实现的 HTTP Web 框架。接口简洁,性能极高。截止 1.4.0 版本,包含测试代码,仅14K,其中测试代码 9K 左右,也就是说框架源码仅 5K 左右。

$ find . -name "*_test.go" | xargs cat | wc -l
8657
$ find . -name "*.go" | xargs cat | wc -l
14115

Gin 特性

  • 快速:路由不使用反射,基于Radix树,内存占用少。

  • 中间件:HTTP请求,可先经过一系列中间件处理,例如:Logger,Authorization,GZIP等。这个特性和 NodeJs 的 Koa 框架很像。中间件机制也极大地提高了框架的可扩展性。

  • 异常处理:服务始终可用,不会宕机。Gin 可以捕获 panic,并恢复。而且有极为便利的机制处理HTTP请求过程中发生的错误。

  • JSON:Gin可以解析并验证请求的JSON。这个特性对Restful API的开发尤其有用。

  • 路由分组:例如将需要授权和不需要授权的API分组,不同版本的API分组。而且分组可嵌套,且性能不受影响。

  • 渲染内置:原生支持JSON,XML和HTML的渲染。

安装Go & Gin

初学者建议先阅读 Go 语言简明教程
一篇文章介绍了 Go 基本类型,结构体,单元测试,并发编程,依赖管理等内容。Go 1.13 以上版本的安装推荐该教程的方式。

  • 安装 Go (Ubuntu)
$ sudo apt-get install golang-go
$ go version
# go version go1.6.2 linux/amd64

Ubuntu自带版本太老了,安装新版可以使用如下命令。

$ sudo add-apt-repository ppa:gophers/archive
$ sudo apt-get update
$ sudo apt-get install golang-1.11-go

默认安装在/usr/lib/go-1.11,需要将/usr/lib/go-1.11/bin手动加入环境变量。在 .bashrc 中添加下面的配置,并 source ~/.bashrc

export PATH=$PATH:/usr/lib/go-1.11/bin

参考:Golang Ubuntu - Github

  • 安装 Go (Mac)
$ brew install go
$ go version
# go version go1.12.5 darwin/amd64
  • 设置环境变量

在 ~/.bashrc 中添加 GOPATH 变量

export GOPATH=~/go
export PATH=$PATH:$GOPATH/bin

添加完后,source ~/.bashrc

  • 安装一些辅助的工具库

由于网络原因,不能够直接访问 golang.org,但相关的库已经镜像到 Golang - Github

例如,直接安装 go-outline 时会报网络错误,因为golang.org/x/toolsgo-outline的依赖库。

$ go get -u -v github.com/ramya-rao-a/go-outline
github.com/ramya-rao-a/go-outline (download)
Fetching https://golang.org/x/tools/go/buildutil?go-get=1
https fetch failed: Get https://golang.org/x/tools/go/buildutil?go-get=1:
dial tcp 216.239.37.1:443: i/o timeout

因此,可以先从 Github 手动安装好,再安装 go-outlinegoreturns

git clone https://github.com/golang/tools.git $GOPATH/src/golang.org/x/tools
go get -v github.com/ramya-rao-a/go-outline
go get -v github.com/sqs/goreturns
go get -v github.com/rogpeppe/godef

Go语言有大量的辅助工具,如果你使用VSCode,将会提示你将必要的工具,例如静态检查、自动补全等工具依次安装完毕。

  • 安装 Gin
go get -u -v github.com/gin-gonic/gin

-v:打印出被构建的代码包的名字
-u:已存在相关的代码包,强行更新代码包及其依赖包

第一个Gin程序

在一个空文件夹里新建文件main.go

// geektutu.com
// main.go
package main

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

func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello, Geektutu")
})
r.Run() // listen and serve on 0.0.0.0:8080
}
  1. 首先,我们使用了gin.Default()生成了一个实例,这个实例即 WSGI 应用程序。
  2. 接下来,我们使用r.Get("/", ...)声明了一个路由,告诉 Gin 什么样的URL 能触发传入的函数,这个函数返回我们想要显示在用户浏览器中的信息。
  3. 最后用 r.Run()函数来让应用运行在本地服务器上,默认监听端口是 _8080_,可以传入参数设置端口,例如r.Run(":9999")即运行在 _9999_端口。
$ go run main.go
[GIN-debug] GET / --> 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
  • 浏览器访问 http://localhost:8080

Hello Gin

路由(Route)

路由方法有 GET, POST, PUT, PATCH, DELETEOPTIONS,还有Any,可匹配以上任意类型的请求。

// 无参数
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Who are you?")
})
$ curl http://localhost:9999/
Who are you?

curl参数可参考https://man.linuxde.net/curl

解析路径参数

有时候我们需要动态的路由,如 /user/:name,通过调用不同的 url 来传入不同的 name。/user/:name/*role* 代表可选。

// 匹配 /user/geektutu
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
$ curl http://localhost:9999/user/geektutu
Hello geektutu

获取Query参数

// 匹配users?name=xxx&role=xxx,role可选
r.GET("/users", func(c *gin.Context) {
name := c.Query("name")
role := c.DefaultQuery("role", "teacher")
c.String(http.StatusOK, "%s is a %s", name, role)
})
$ curl "http://localhost:9999/users?name=Tom&role=student"
Tom is a student

获取POST参数

// POST
r.POST("/form", func(c *gin.Context) {
username := c.PostForm("username")
password := c.DefaultPostForm("password", "000000") // 可设置默认值

c.JSON(http.StatusOK, gin.H{
"username": username,
"password": password,
})
})
$ curl http://localhost:9999/form  -X POST -d 'username=geektutu&password=1234'
{"password":"1234","username":"geektutu"}

Query和POST混合参数

// GET 和 POST 混合
r.POST("/posts", func(c *gin.Context) {
id := c.Query("id")
page := c.DefaultQuery("page", "0")
username := c.PostForm("username")
password := c.DefaultPostForm("username", "000000") // 可设置默认值

c.JSON(http.StatusOK, gin.H{
"id": id,
"page": page,
"username": username,
"password": password,
})
})
$ curl "http://localhost:9999/posts?id=9876&page=7"  -X POST -d 'username=geektutu&password=1234'
{"id":"9876","page":"7","password":"1234","username":"geektutu"}

Map参数(字典参数)

r.POST("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
names := c.PostFormMap("names")

c.JSON(http.StatusOK, gin.H{
"ids": ids,
"names": names,
})
})
$ curl -g "http://localhost:9999/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David'
{"ids":{"Jack":"001","Tom":"002"},"names":{"a":"Sam","b":"David"}}

重定向(Redirect)

r.GET("/redirect", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/index")
})

r.GET("/goindex", func(c *gin.Context) {
c.Request.URL.Path = "/"
r.HandleContext(c)
})
$ curl -i http://localhost:9999/redirect
HTTP/1.1 301 Moved Permanently
Content-Type: text/html; charset=utf-8
Location: /
Date: Thu, 08 Aug 2019 17:22:14 GMT
Content-Length: 36

<a href="/">Moved Permanently</a>.

$ curl "http://localhost:9999/goindex"
Who are you?

分组路由(Grouping Routes)

如果有一组路由,前缀都是/api/v1开头,是否每个路由都需要加上/api/v1这个前缀呢?答案是不需要,分组路由可以解决这个问题。利用分组路由还可以更好地实现权限控制,例如将需要登录鉴权的路由放到同一分组中去,简化权限控制。

// group routes 分组路由
defaultHandler := func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"path": c.FullPath(),
})
}
// group: v1
v1 := r.Group("/v1")
{
v1.GET("/posts", defaultHandler)
v1.GET("/series", defaultHandler)
}
// group: v2
v2 := r.Group("/v2")
{
v2.GET("/posts", defaultHandler)
v2.GET("/series", defaultHandler)
}
$ curl http://localhost:9999/v1/posts
{"path":"/v1/posts"}
$ curl http://localhost:9999/v2/posts
{"path":"/v2/posts"}
r.POST("/upload1", func(c *gin.Context) {
file, _ := c.FormFile("file")
// c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, "%s uploaded!", file.Filename)
})
r.POST("/upload2", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["upload[]"]

for _, file := range files {
log.Println(file.Filename)
// c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, "%d files uploaded!", len(files))
})

HTML模板(Template)

type student struct {
Name string
Age int8
}

r.LoadHTMLGlob("templates/*")

stu1 := &student{Name: "Geektutu", Age: 20}
stu2 := &student{Name: "Jack", Age: 22}
r.GET("/arr", func(c *gin.Context) {
c.HTML(http.StatusOK, "arr.tmpl", gin.H{
"title": "Gin",
"stuArr": [2]*student{stu1, stu2},
})
})
<!-- templates/arr.tmpl -->
<html>
<body>
<p>hello, {{.title}}</p>
{{range $index, $ele := .stuArr }}
<p>{{ $index }}: {{ $ele.Name }} is {{ $ele.Age }} years old</p>
{{ end }}
</body>
</html>
$ curl http://localhost:9999/arr

<html>
<body>
<p>hello, Gin</p>
<p>0: Geektutu is 20 years old</p>
<p>1: Jack is 22 years old</p>
</body>
</html>
  • Gin默认使用模板Go语言标准库的模板text/templatehtml/template,语法与标准库一致,支持各种复杂场景的渲染。
  • 参考官方文档text/templatehtml/template

中间件(Middleware)

// 作用于全局
r.Use(gin.Logger())
r.Use(gin.Recovery())

// 作用于单个路由
r.GET("/benchmark", MyBenchLogger(), benchEndpoint)

// 作用于某个组
authorized := r.Group("/")
authorized.Use(AuthRequired())
{
authorized.POST("/login", loginEndpoint)
authorized.POST("/submit", submitEndpoint)
}

如何自定义中间件呢?

func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// 给Context实例设置一个值
c.Set("geektutu", "1111")
// 请求前
c.Next()
// 请求后
latency := time.Since(t)
log.Print(latency)
}
}

热加载调试 Hot Reload

Python 的 Flask框架,有 debug 模式,启动时传入 debug=True 就可以热加载(Hot Reload, Live Reload)了。即更改源码,保存后,自动触发更新,浏览器上刷新即可。免去了杀进程、重新启动之苦。

Gin 原生不支持,但有很多额外的库可以支持。例如

  • github.com/codegangsta/gin
  • github.com/pilu/fresh

这次,我们采用 github.com/pilu/fresh

go get -v -u github.com/pilu/fresh

安装好后,只需要将go run main.go命令换成fresh即可。每次更改源文件,代码将自动重新编译(Auto Compile)。

参考 github.com/pilu/fresh - Github


专题: Go 简明教程

本文发表于 2019-08-07,最后修改于 2022-04-21。

本站永久域名「 geektutu.com 」,也可搜索「 极客兔兔 」找到我。


上一篇 « Go 语言简明教程 下一篇 » 7天用Go从零实现Web框架Gee教程


Recommend

  • 143

    玩转异步 JS :async/await 简明教程(附视频下载) 2017年11月10日 00:39 ·  阅读 8188 ...

  • 56

  • 75
    • bridgeforyou.cn 6 years ago
    • Cache

    Kafka简明教程 - Bridge for You

    Bridge for You

  • 112

    简明Python教程可以作为 Python 编程语言的一本指南或者教程。它主要是为新手而设计,不过对于有经验的程序员来说,它同样有用。 即便你对计算机的了解只是如何在计算机上保存文本文件,你都可以通过本书学习 Python。如果你有编程经验,你也可以使用本...

  • 54

    linux命令行简明教程 pdf,学习Linux的朋友必看,命令行在Linux中是经常用到的,了解这些命令是提高操作效能的基础.

  • 42
    • jiajunhuang.com 6 years ago
    • Cache

    CGO简明教程

  • 37

    Go模块是Go语言的依赖包管理工具。 Go模块的使用 1、Go1.11及以后版本才能使用。 mod是模块英文modules的简写。 列举一些常用的命令行: go help mod 查看帮助...

  • 69
    • 掘金 juejin.im 6 years ago
    • Cache

    超详实Git简明教程与命令大全

    Git (wiki: en chs )是一个免费开源的分布式版本控制系统,由linux内核作者linus Torvalds开发,大型开源项目linux kernel、Android、chromium、mono、dotnet、UE4等都使用Git管理项目著名g

  • 41
    • jiajunhuang.com 6 years ago
    • Cache

    Gunicorn 简明教程

    Gunicorn是Python下的一个WSGI服务器,听起来很普通,不过因为它支持很多特性,在 Python界还是很流行,例如作为起Flask的父进程,支持用gevent把Flask打个patch等。 它使用的是pre-fork的模式,即启动的时候fork出n个进程,然后...

  • 44
    • jiajunhuang.com 6 years ago
    • Cache

    ansible 简明教程

    ini格式的配置文件大家都懂吧。 先编写一个host文件例如: [local] 127.0.0.1 保存为 localhost.ini 然后执行 ansible -i ./test.host.ini local -k -a 'ls'

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK