原文鏈接:https://medium.com/@cgrant/developing-a-simple-crud-api-with-go-gin-and-gorm-df87d98e6ed1
翻譯:devabel
介紹
Golang是一種令人興奮的語言,但新手可能會(huì)被新的語法和各種框架所淹沒。基礎(chǔ)知識(shí)入門可能會(huì)成為一項(xiàng)挑戰(zhàn)。
在這個(gè)例子中,我想展示創(chuàng)建功能API所需的最少代碼。我們將開發(fā)一個(gè)簡單的API,為基本模型提供創(chuàng)建,讀取,更新和刪除(CRUD)功能。使用和對(duì)象關(guān)系映射(ORM)工具,我們將能夠快速更新我們的數(shù)據(jù)模型,所有數(shù)據(jù)模型都在100行代碼之下。所以讓我們開始吧。
在https://github.com/cgrant/gin-gorm-api-example可以找到這篇文章的所有代碼。
入門
這個(gè)例子假設(shè)你已經(jīng)安裝并運(yùn)行g(shù)o語言的環(huán)境。如果您還沒有安裝,請(qǐng)轉(zhuǎn)到http://cgrant.io/tutorials/go/getting-started-with-go/獲取快速入門。
Gin Web框架
由于我們將通過HTTP提供我們的API,因此我們需要一個(gè)Web框架來處理路由并提供請(qǐng)求。有許多框架可用,具有不同的功能和性能指標(biāo)。在這個(gè)例子中,我們將使用Gin框架https://github.com/gin-gonic/gin 。由于速度和簡單性,Gin是API開發(fā)的一個(gè)很好的框架。
首先,讓我們?cè)? GOPATH / src / simple-api中為我們的服務(wù)創(chuàng)建一個(gè)新文件夾,然后添加一個(gè)main.go文件,如下所示
package main
import “fmt”
func main() {
fmt.Println(“Hello World”)
}
在我們繼續(xù)學(xué)習(xí)前,讓我們測試一下,確保一切正常運(yùn)行。
$ go run main.go
Hello World
程序運(yùn)行正常?,F(xiàn)在讓我們使用Gin框架將它變成一個(gè)Web應(yīng)用程序。
package main
import “github.com/gin-gonic/gin”
func main() {
r := gin.Default()
r.GET(“/”, func(c *gin.Context) {
c.String(200, “Hello World”)
})
r.Run()
}
保存并運(yùn)行它
$ go run main.go
[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 / → 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
[GIN] 2016/12/02–14:57:52 | 200 | 33.798μs | ::1 | GET /
然后瀏覽器訪問http://localhost:8080/
Hello World
成功!?。?/p>
我們正在構(gòu)建一個(gè)API,但不是一個(gè)Web應(yīng)用程序,所以讓我們將其切換到JSON響應(yīng)
package main
import “github.com/gin-gonic/gin”
func main() {
r := gin.Default()
r.GET(“/”, func(c *gin.Context) {
c.JSON(200, gin.H{
“message”: “Hello World”,
})
})
r.Run() // listen and server on 0.0.0.0:8080
}
保存文件,重新運(yùn)行并刷新瀏覽器,您應(yīng)該看到我們的JSON 消息
{“message”:“Hello World”}
GORM的數(shù)據(jù)持久性
現(xiàn)在讓我們看看我們的持久層。對(duì)于本節(jié)我們將使用本地的基于SQLite文件的數(shù)據(jù)庫開始。稍后我們將切換到使用MySql來證明。
Gorm http://jinzhu.me/gorm/是一個(gè)用于go的對(duì)象關(guān)系映射(ORM)框架。它大大簡化了模型到數(shù)據(jù)庫的映射和持久化。盡管我不是大型復(fù)雜系統(tǒng)的ORM的忠實(shí)擁躉,但他們對(duì)原型開發(fā)新的綠地應(yīng)用程序確實(shí)很有效。Gorm在Go領(lǐng)域是一個(gè)非常受歡迎的工具,我們將在這里看看它。
為了解決gorm問題,我們將把我們剛寫的Gin代碼換掉,并且演示一下gorm的功能。學(xué)習(xí)完這塊,我們會(huì)將Gin重新添加到應(yīng)用程序中。
讓我們開始一個(gè)小例子。
package main
import (
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
func main() {
db, _ := gorm.Open(“sqlite3”, “./gorm.db”)
defer db.Close()
}
如果你現(xiàn)在運(yùn)行這個(gè)程序,你會(huì)在你的文件系統(tǒng)中看到一個(gè)名為gorm.db的新文件。這是我們的數(shù)據(jù)庫文件系統(tǒng)將在應(yīng)用程序中使用。雖然我們可以看到我們的應(yīng)用程序正在運(yùn)行,而且gorm正在被使用,但我們的系統(tǒng)還沒有完成。讓我們添加更多的代碼。
package main
import (
“fmt”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
}
func main() {
db, _ := gorm.Open(“sqlite3”, “./gorm.db”)
defer db.Close()
p1 := Person{FirstName: “John”, LastName: “Doe”}
p2 := Person{FirstName: “Jane”, LastName: “Smith”}
fmt.Println(p1.FirstName)
fmt.Println(p2.LastName)
}
在這里,我們剛剛添加了一個(gè)簡單的Person結(jié)構(gòu),并在使用它們打印出值之前創(chuàng)建了一些實(shí)例。請(qǐng)記住,Person結(jié)構(gòu)中的字段需要以大寫字母開頭,因?yàn)镚o定義這些是公共字段。
現(xiàn)在我們已經(jīng)有了一個(gè)可以使用Gorm的對(duì)象。
package main
import (
“fmt”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
}
func main() {
db, _ := gorm.Open(“sqlite3”, “./gorm.db”)
defer db.Close()
db.AutoMigrate(&Person{})
p1 := Person{FirstName: “John”, LastName: “Doe”}
p2 := Person{FirstName: “Jane”, LastName: “Smith”}
db.Create(&p1)
var p3 Person // identify a Person type for us to store the results in
db.First(&p3) // Find the first record in the Database and store it in p3
fmt.Println(p1.FirstName)
fmt.Println(p2.LastName)
fmt.Println(p3.LastName) // print out our record from the database
}
現(xiàn)在讓我們運(yùn)行它,看看輸出什么
$ go run main.go
John
Smith
Doe
哇,非常簡單。只需幾行代碼,我們就可以保存并從數(shù)據(jù)庫中檢索。Gorm在如何存儲(chǔ)和查詢他們的網(wǎng)站上有更多的選項(xiàng)。接下來我們將介紹幾個(gè)核心部分,但請(qǐng)查看他們的文檔以獲取更多選項(xiàng)。
制作API
我們已經(jīng)回顧了框架如何獨(dú)立運(yùn)作。現(xiàn)在是時(shí)候把所有東西放在一起成為一個(gè)可用的API
查詢所有信息
讓我們從查閱我們之前添加的數(shù)據(jù)開始閱讀CRUD的部分。我將刪除我們剛剛通過的一些行,并用一個(gè)新路由添加到Gin框架中,以查詢我們的數(shù)據(jù)庫。
package main
import (
“fmt”
“github.com/gin-gonic/gin”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
var db *gorm.DB
var err error
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
db, err = gorm.Open(“sqlite3”, “./gorm.db”)
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(“/”, GetProjects)
r.Run(“:8080”)
}
func GetProjects(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
現(xiàn)在運(yùn)行它并轉(zhuǎn)到http://localhost:8080/,你應(yīng)該看到
[{“id”: 1,”firstname”: “John”,”lastname”: “Doe”}]
哇只是幾行代碼,我們已經(jīng)獲得API響應(yīng)。大部分都是錯(cuò)誤處理!
查詢單條信息
OK讓我們以REST為導(dǎo)向更新上下文,并增加查找單條信息的功能。
package main
import (
“fmt”
“github.com/gin-gonic/gin”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
var db *gorm.DB
var err error
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
db, err = gorm.Open(“sqlite3”, “./gorm.db”)
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(“/people/”, GetPeople)
r.GET(“/people/:id”, GetPerson)
r.Run(“:8080”)
}
func GetPerson(c *gin.Context) {
id := c.Params.ByName(“id”)
var person Person
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, person)
}
}
func GetPeople(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
現(xiàn)在運(yùn)行服務(wù)器,但請(qǐng)注意,我們更改了上下文,現(xiàn)在您將轉(zhuǎn)到http:// localhost:8080 / people /查看您的列表。一旦將該ID添加到網(wǎng)址的末尾,您將得到單個(gè)記錄返回http:// localhost:8080 / people / 1
{“id”: 1,”firstname”: “John”,”lastname”: “Doe”}
創(chuàng)建
很難用僅有一條記錄來看出差異。很難區(qū)分[{...}]和{...}之間的區(qū)別所以讓我們添加Create函數(shù)和路由
package main
import (
“fmt”
“github.com/gin-gonic/gin”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
var db *gorm.DB
var err error
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
db, err = gorm.Open(“sqlite3”, “./gorm.db”)
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(“/people/”, GetPeople)
r.GET(“/people/:id”, GetPerson)
r.POST(“/people”, CreatePerson)
r.Run(“:8080”)
}
func CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
func GetPerson(c *gin.Context) {
id := c.Params.ByName(“id”)
var person Person
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, person)
}
}
func GetPeople(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
現(xiàn)在要測試這一個(gè),我們將從命令行運(yùn)行一個(gè)curl命令。我們還需要運(yùn)行服務(wù)器,以便打開另一個(gè)終端窗口,以便可以運(yùn)行這兩個(gè)命令。使用$ go run main.go在第一個(gè)窗口中運(yùn)行服務(wù)器
一旦運(yùn)行,在第二個(gè)窗口運(yùn)行:
$ curl -i -X POST http://localhost:8080/people -d ‘{ “FirstName”: “Elvis”, “LastName”: “Presley”}’
你應(yīng)該看到一個(gè)成功的回應(yīng)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sat, 03 Dec 2016 00:14:06 GMT
Content-Length: 50
{“id”:2,”firstname”:”Elvis”,”lastname”:”Presley”}
現(xiàn)在讓我們?cè)跒g覽器中列出我們的人員,看看它是否列出了我們所有的條目
http:// localhost:8080 / people /
[{“id”: 1,”firstname”: “John”,”lastname”: “Doe”},{“id”: 2,”firstname”: “Elvis”,”lastname”: “Presley”}]
太棒了,輸出結(jié)果!看到這里你認(rèn)為這很酷。
這次只發(fā)送部分Person數(shù)據(jù)
$ curl -i -X POST [http://localhost:8080/people](http://localhost:8080/people) -d ‘{ “FirstName”: “Madison”}’
刷新瀏覽器并查看它只添加了我們發(fā)送的數(shù)據(jù)
[{“id”: 1,”firstname”: “John”,”lastname”: “Doe”},{“id”: 2,”firstname”: “Elvis”,”lastname”: “Presley”},{“id”: 3,”firstname”: “Madison”,”lastname”: “”}]
這是Gin的一部分,請(qǐng)注意CreatePerson函數(shù)中的c.BindJSON(&person)行。它會(huì)自動(dòng)填充請(qǐng)求中的任何匹配數(shù)據(jù)字段。
你也可能錯(cuò)過了它,但我的數(shù)據(jù)庫中的情況和我通過的情況是不同的。Gin 對(duì)大小寫不敏感。我傳入了FirstName ,但數(shù)據(jù)庫使用了firstname.。
很簡單!
更新
盡管如此,我們不能讓Madison的last name為空。是時(shí)候添加我們的更新功能了
package main
import (
“fmt”
“github.com/gin-gonic/gin”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
var db *gorm.DB
var err error
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
db, err = gorm.Open(“sqlite3”, “./gorm.db”)
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(“/people/”, GetPeople)
r.GET(“/people/:id”, GetPerson)
r.POST(“/people”, CreatePerson)
r.PUT(“/people/:id”, UpdatePerson)
r.Run(“:8080”)
}
func UpdatePerson(c *gin.Context) {
var person Person
id := c.Params.ByName(“id”)
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
}
c.BindJSON(&person)
db.Save(&person)
c.JSON(200, person)
}
func CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
func GetPerson(c *gin.Context) {
id := c.Params.ByName(“id”)
var person Person
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, person)
}
}
func GetPeople(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
為了測試這個(gè),我們將使用一個(gè)類似的curl命令,但是我們會(huì)在特定的用戶上調(diào)用PUT方法
$ curl -i -X PUT http://localhost:8080/people/3 -d ‘{ “FirstName”: “Madison”, “LastName”:”Sawyer” }’
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sat, 03 Dec 2016 00:25:35 GMT
Content-Length: 51
{“id”:3,”firstname”:”Madison”,”lastname”:”Sawyer”}
果然,如果我們刷新我們的瀏覽器,我們看到它添加了Sawyer這條數(shù)據(jù)。
[{“id”: 1,”firstname”: “John”,”lastname”: “Doe”},{“id”: 2,”firstname”: “Elvis”,”lastname”: “Presley”},{“id”: 3,”firstname”: “Madison”,”lastname”: “Sawyer”}]
我們?cè)俅慰梢园l(fā)送部分?jǐn)?shù)據(jù)進(jìn)行部分更新
$ curl -i -X PUT http://localhost:8080/people/3 -d ‘{ “FirstName”: “Tom” }’
顯示為
[{“id”: 1,”firstname”: “John”,”lastname”: “Doe”},{“id”: 2,”firstname”: “Elvis”,”lastname”: “Presley”},{“id”: 3,”firstname”: “Tom”,”lastname”: “Sawyer”}]
刪除
現(xiàn)在要完成該解決方案讓dd在刪除功能
package main
import (
“fmt”
“github.com/gin-gonic/gin”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
var db *gorm.DB
var err error
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
db, err = gorm.Open(“sqlite3”, “./gorm.db”)
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(“/people/”, GetPeople)
r.GET(“/people/:id”, GetPerson)
r.POST(“/people”, CreatePerson)
r.PUT(“/people/:id”, UpdatePerson)
r.DELETE(“/people/:id”, DeletePerson)
r.Run(“:8080”)
}
func DeletePerson(c *gin.Context) {
id := c.Params.ByName(“id”)
var person Person
d := db.Where(“id = ?”, id).Delete(&person)
fmt.Println(d)
c.JSON(200, gin.H{“id #” + id: “deleted”})
}
func UpdatePerson(c *gin.Context) {
var person Person
id := c.Params.ByName(“id”)
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
}
c.BindJSON(&person)
db.Save(&person)
c.JSON(200, person)
}
func CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
func GetPerson(c *gin.Context) {
id := c.Params.ByName(“id”)
var person Person
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, person)
}
}
func GetPeople(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
為了測試這個(gè),我們將使用帶curl的Delete方法來調(diào)用它
$ curl -i -X DELETE http://localhost:8080/people/1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sat, 03 Dec 2016 00:32:40 GMT
Content-Length: 20
{“id #1”:”deleted”}
刷新瀏覽器,你會(huì)看到我們的John Doe已被刪除
[{“id”: 2,”firstname”: “Elvis”,”lastname”: “Presley”},{“id”: 3,”firstname”: “Tom”,”lastname”: “Sawyer”}]
修改模型
在定義基本的API后,現(xiàn)在是開始改變Person對(duì)象的好時(shí)機(jī)。我們可以通過只更改person結(jié)構(gòu)來輕松修改數(shù)據(jù)庫和api。
我要做的就是在Person Struct中添加一個(gè)city字段。沒有其他的,就一行。
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
City string `json:”city”`
}
刷新我們的瀏覽器并拉出列表,您可以看到我所有的對(duì)象現(xiàn)在都有city
[{“id”: 2,”firstname”: “Elvis”,”lastname”: “Presley”,”city”: “”},{“id”: 3,”firstname”: “Tom”,”lastname”: “Sawyer”,”city”: “”}]
我可以創(chuàng)建并更新新字段,而無需進(jìn)行其他更改
$ curl -i -X PUT http://localhost:8080/people/2 -d ‘{ “city”: “Memphis” }’
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sat, 03 Dec 2016 00:40:57 GMT
Content-Length: 67
{“id”:2,”firstname”:”Elvis”,”lastname”:”Presley”,”city”:”Memphis”}
這全部由我們主函數(shù)中的db.AutoMigrate(&Person {})行處理。在生產(chǎn)環(huán)境中,我們希望更接近地管理架構(gòu),但對(duì)于原型來說,這是完美的
使用MySql
好的,我明白,沒問題,你想使用MySql而不是SQLite。
為此,我們只需要修改一個(gè)導(dǎo)入聲明和連接..
導(dǎo)入_“github.com/go-sql-driver/mysql”
連接
db, _ = gorm.Open(“mysql”, “user:pass@tcp(127.0.0.1:3306)/samples?charset=utf8&parseTime=True&loc=Local”)
完整的例子
package main
// only need mysql OR sqlite
// both are included here for reference
import (
“fmt”
“github.com/gin-gonic/gin”
_ “github.com/go-sql-driver/mysql”
“github.com/jinzhu/gorm”
_ “github.com/jinzhu/gorm/dialects/sqlite”
)
var db *gorm.DB
var err error
type Person struct {
ID uint `json:”id”`
FirstName string `json:”firstname”`
LastName string `json:”lastname”`
City string `json:”city”`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
//db, err = gorm.Open(“sqlite3”, “./gorm.db”)
db, _ = gorm.Open(“mysql”, “user:pass@tcp(127.0.0.1:3306)/database?charset=utf8&parseTime=True&loc=Local”)
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(“/people/”, GetPeople)
r.GET(“/people/:id”, GetPerson)
r.POST(“/people”, CreatePerson)
r.PUT(“/people/:id”, UpdatePerson)
r.DELETE(“/people/:id”, DeletePerson)
r.Run(“:8080”)
}
func DeletePerson(c *gin.Context) {
id := c.Params.ByName(“id”)
var person Person
d := db.Where(“id = ?”, id).Delete(&person)
fmt.Println(d)
c.JSON(200, gin.H{“id #” + id: “deleted”})
}
func UpdatePerson(c *gin.Context) {
var person Person
id := c.Params.ByName(“id”)
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
}
c.BindJSON(&person)
db.Save(&person)
c.JSON(200, person)
}
func CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
func GetPerson(c *gin.Context) {
id := c.Params.ByName(“id”)
var person Person
if err := db.Where(“id = ?”, id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, person)
}
}
func GetPeople(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
結(jié)論
Go是一個(gè)靈活的語言,具有強(qiáng)大的環(huán)境。它非常容易使用少量代碼快速構(gòu)建功能豐富的應(yīng)用程序。我希望這是一個(gè)有用的練習(xí)。請(qǐng)隨時(shí)分享您的想法和問題。