本实验手册旨在帮助学员使用 Gin 框架来开发微服务。 Gin 是 Go 语言生态环境中比较出名的 Web 框架,具有简单,高性能,开源等特点。
在第一次练习中,我们首先搭建好项目框架,然后实现一个简单的 rest 服务。
建立项目 新建一个名为: go-todo-api-srv 的目录,用来存放所有项目相关的文件。
进入该目录
因为我们使用 go modules 来管理依赖,因此首先初始化模块,执行:
1 go mod init go-todo-api-srv
如果正常,系统会在目录中新建一个名为: go.mod 的文件,内容如下:
1 2 3 module go-todo-api-srv go 1.14
第一行是我们命名的模块名,第三行是当前项目使用的 go 的版本。
在项目中引用 Gin 库 为了在项目中引用 Gin 库,我们可以使用 go get 命令, 执行:
1 go get github.com/gin-gonic/gin
如果正常,系统显示:
1 go: github.com/gin-gonic/gin upgrade => v1.6.2
写本文时 Gin 的最新版本为 1.6.2
查看 go.mod 文件,会看到文件的末尾加上了对 gin 库依赖的声明:
1 2 3 4 5 module go-todo-api-srv go 1.14 require github.com/gin-gonic/gin v1.6.2 // indirect
同时,我们可以看到在项目中多生成了一个名为: go.sum 的文件,里面包含了具体的依赖库的声明及间接依赖库声明,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
建立实体类型 在 Go 语言中,没有在其它语言(比如: Java, C#等)中的类,因此为表现一个业务实体的数据我们使用结构类型。为方便以后扩展,我们将所有的业务实体类型放在 entity 目录中。
在项目目录中新建 entity 目录
在 entity 目录中新建名为: Todo.go 的文件, 内容如下:
1 2 3 4 5 6 7 8 9 package entitytype Todo struct { ID uint `json:"id"` Title string `json:"title"` Desc string `json:"desc"` Done bool `json:"done"` }
建立启动程序 在项目目录中新建名为: main.go 的文件,建立后,整个项目的文件和目录包括:
1 2 3 4 5 6 go-todo-api-srv/ + entity Todo.go main.go go.mod go.sum
在 main.go 中引入对 gin 和自建 entity 的引用:
1 2 3 4 import ( "github.com/gin-gonic/gin" "go-todo-api-srv/entity" )
注意:在引入自定义的entity包时,需要使用模块名加上包名
因为在第一次练习中,还没有引入数据库,因此先直接声明一个数组来模拟服务返回的数据:
1 2 3 var todos = []entity.Todo { entity.Todo{ID: 1 , Title: "call tom" , Desc: "desc of todo 1" , Done: false }, }
最后,新建 main 函数,启动服务:
1 2 3 4 5 6 7 8 9 10 func main () { r := gin.Default() r.GET("/api/todo" , func (c *gin.Context) { c.JSON(http.StatusOK, todos) }) r.Run() }
可以看到,我们使用 Gin 的路由功能建立了一个路由, 路径是: “/api/todo”。 当用户访问该路径时,对应的函数将被执行,当前只是简单的以JSON形式返回数据。
完整的 main.go 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "net/http" "github.com/gin-gonic/gin" "go-todo-api-srv/entity" ) var todos = []entity.Todo { entity.Todo{ID: 1 , Title: "call tom" , Desc: "desc of todo 1" , Done: false }, } func main () { r := gin.Default() r.GET("/api/todo" , func (c *gin.Context) { c.JSON(http.StatusOK, todos) }) r.Run() }
运行项目 在项目目录中执行:
如果正常,可以看到如下的系统输出:
1 2 3 4 5 6 7 8 9 [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 /api/todo --> 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
可以看到,服务已经启动并在本地的 8080 端口进行监听。
打开浏览器,访问:
1 http://localhost:8080/api/todo
可以在浏览器中看到如下内容:
1 2 3 4 5 6 7 8 [ { "id" : 1 , "title" : "call tom" , "desc" : "desc of todo 1" , "done" : false } ]