(原文作者:Christopher Grant)
介绍
Golang是一种令人兴奋的语言,但是新来者可能会被新的语义和可用的各种框架所淹没。入门入门可能会是一个挑战。
在此示例中,我想展示创建功能性api所需的最少代码集。我们将开发一个简单的API,该API为基本模型提供创建,读取,更新和删除(CRUD)功能。使用和对象关系映射(ORM)工具,我们可以使用100行以下的新字段快速更新数据模型。因此,让我们开始吧。
最终代码可以在这里找到https://github.com/cgrant/gin-gorm-api-example
入门
本示例假定您已经安装并运行了Go。如果仍然需要设置,请转到http://cgrant.io/tutorials/go/getting-started-with-go/快速入门。
带有Gin的Web框架
由于我们将通过HTTP提供API,因此我们需要一个Web框架来处理路由和服务请求。有许多具有不同功能和性能提升的框架。对于此示例,我们将使用Gin Web框架https://github.com/gin-gonic/gin。由于Gin的速度和简便性,它是API开发的一个不错的框架。
首先,让我们在$ GOPATH/src/simple-api中为我们的服务创建一个新文件夹,并添加一个main.go文件,如下所示
packagemainimportfmtfuncmain(){
fmt.Println(Hello World)
}
在我们走得太远之前,让我们对其进行测试以确保一切正常运行。
$gorun main.goHello World
完美,我们都准备好了。现在,让我们使用Gin框架将其制作到Web应用程序中。
package mainimportgithub.com/gin-gonic/ginfuncmain(){
r := gin.Default()
r.GET(/,func(c*gin.Context){c.String(200,HelloWorld)
})
r.Run()
}
保存并运行
$ go run main.go
[GIN-debug] [WARNING] Running in debug mode. Switch toreleasemodeinproduction.
—usingenv:exportGIN_MODE=release—usingcode: gin.SetMode(gin.ReleaseMode)
[GIN-debug]GET/ → main.main.func1 (3handlers)
[GIN-debug] EnvironmentvariablePORTisundefined.Usingport :8080bydefault[GIN-debug] ListeningandservingHTTPon:8080[GIN]2016/12/02–14:57:52|200|33.798µs | ::1|GET/
然后浏览到http://localhost:8080
HelloWorld
成功!!!
我们正在构建一个API(尽管不是Web应用程序),因此我们将其切换为JSON响应
package mainimportgithub.com/gin-gonic/ginfuncmain(){
r := gin.Default()
r.GET(/,func(c*gin.Context){c.JSON(200, gin.H{
message:HelloWorld,
})
})
r.Run()// listen and server on 0.0.0.0:8080}
保存文件,重新运行服务器并刷新浏览器,您应该将我们的消息显示为JSON{message: Hello World}
GORM的数据持久性
现在让我们看一下持久层。在本节中,我们将从基于SQLite文件的本地数据库开始,您可以在本地使用该数据库。稍后,我们将其切换为使用MySql进行演示。
Gorm http://jinzhu.me/gorm/是go的对象关系映射(ORM)框架。它极大地简化了模型到数据库的映射和持久性。尽管我不是大型复杂系统的ORM的忠实拥护者,但它们确实可以很好地用于新绿地应用的原型设计。Gorm在Go空间中是一个流行的工具,我们将在这里进行介绍。
为了遍历gorm,我们将交换刚刚编写的Gin代码,并稍微演示一下gorm的功能。一旦我们浏览了关键元素,我们就将Gin重新添加到应用程序中。
让我们从一个小例子开始。
packagemainimport(
github.com/jinzhu/gorm
_ github.com/jinzhu/gorm/dialects/sqlite
)funcmain(){
db, _ := gorm.Open(sqlite3, ./gorm.db)deferdb.Close()
}
如果立即运行此命令,将会在文件系统中看到一个名为gorm.db的新文件。这是系统将在应用程序中使用的我们的数据库文件。虽然我们可以看到我们的应用程序正在运行,并且gorm正在被使用,但我们的系统还没有做什么工作。让我们添加更多代码。
packagemainimport(
fmt
github.com/jinzhu/gorm
_ github.com/jinzhu/gorm/dialects/sqlite
)typePersonstruct{
IDuint`json:id`FirstNamestring`json:firstname`LastNamestring`json:lastname`}funcmain(){
db, _ := gorm.Open(sqlite3, ./gorm.db)deferdb.Close()
p1 := Person{FirstName: John, LastName: Doe}
p2 := Person{FirstName: Jane, LastName: Smith}
fmt.Println(p1.FirstName)
fmt.Println(p2.LastName)
}
在这里,我们只是添加了一个简单的Person结构,并创建了几个实例,然后使用它们来打印值。请记住,Person结构上的字段必须以大写字母开头,因为这是Go理解这些是公共字段的方式。
现在我们有了一个可以使用的对象,让我们使用Gorm。
packagemainimport(
fmt
github.com/jinzhu/gorm
_ github.com/jinzhu/gorm/dialects/sqlite
)typePersonstruct{
IDuint`json:id`FirstNamestring`json:firstname`LastNamestring`json:lastname`}funcmain(){
db, _ := gorm.Open(sqlite3, ./gorm.db)deferdb.Close()
db.AutoMigrate(&Person{})
p1 := Person{FirstName: John, LastName: Doe}
p2 := Person{FirstName: Jane, LastName: Smith}
db.Create(&p1)varp3 Person// identify a Person type for us to store the results indb.First(&p3)// Find the first record in the Database and store it in p3fmt.Println(p1.FirstName)
fmt.Println(p2.LastName)
fmt.Println(p3.LastName)// print out our record from the database}
太好了,现在让我们运行它,看看我们有什么
$gorun main.goJohn
Smith
Doe
哇,这很容易。只需几行代码,我们就可以保存和检索数据库。Gorm在如何存储和在其网站上进行查询方面有更多选择。接下来,我们将介绍几个核心部分,但请查看其文档以获取更多选择
制作API
我们已经回顾了框架如何独立运作。现在是时候将所有内容组合到可用的API中了
阅读全部
让我们从查询CRUD的Read部分开始,首先查询我们之前添加的数据。我将删除几行内容,并在Gin框架中添加一条查询数据库的新路线。
packagemainimport(
fmt
github.com/gin-gonic/gin
github.com/jinzhu/gorm
_ github.com/jinzhu/gorm/dialects/sqlite
)vardb *gorm.DBvarerr errortypePersonstruct{
IDuint`json:id`FirstNamestring`json:firstname`LastNamestring`json:lastname`}funcmain(){//NOTE:See we’re using = to assign the global var// instead of := which would assign it only in this functiondb, err = gorm.Open(sqlite3, ./gorm.db)iferr !=nil{
fmt.Println(err)
}deferdb.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(/, GetProjects)
r.Run(:8080)
}funcGetProjects(c *gin.Context){varpeople []Personiferr := db.Find(&people).Error; err !=nil{
c.AbortWithStatus(404)
fmt.Println(err)
}else{
c.JSON(200, people)
}
}
现在运行它并转到http:// localhost:8080,您应该看到
[{id: 1,firstname: John,lastname: Doe}]
哇,只需几行代码,我们已经获得了API响应。其中大多数也是错误处理!
读一个
OK使更新上下文更加面向REST,并增加了查找单个人的能力。
packagemainimport(
fmt
github.com/gin-gonic/gin
github.com/jinzhu/gorm
_ github.com/jinzhu/gorm/dialects/sqlite
)vardb *gorm.DBvarerr errortypePersonstruct{
IDuint`json:id`FirstNamestring`json:firstname`LastNamestring`json:lastname`}funcmain(){//NOTE:See we’re using = to assign the global var// instead of := which would assign it only in this functiondb, err = gorm.Open(sqlite3, ./gorm.db)iferr !=nil{
fmt.Println(err)
}deferdb.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(/people/, GetPeople)
r.GET(/people/:id, GetPerson)
r.Run(:8080)
}funcGetPerson(c *gin.Context){
id := c.Params.ByName(id)varperson Personiferr := db.Where(id = ?, id).First(&person).Error; err !=nil{
c.AbortWithStatus(404)
fmt.Println(err)
}else{
c.JSON(200, person)
}
}funcGetPeople(c *gin.Context){varpeople []Personiferr := db.Find(&people).Error; err !=nil{
c.AbortWithStatus(404)
fmt.Println(err)
}else{
c.JSON(200, people)
}
}
现在运行服务器,但请注意我们已更改了上下文,因此现在您将转到http:// localhost:8080 / people /查看您的列表。在那里,将ID添加到url的末尾,您将获得单条记录http:// localhost:8080 / people / 1
{id: 1,firstname: John,lastname: Doe}
创建
仅使用一个记录就很难看出差异。很难说出[{…}]和{…}之间的区别,所以让我们添加Create函数和路由
packagemainimport(
fmt
github.com/gin-gonic/gin
github.com/jinzhu/gorm
_ github.com/jinzhu/gorm/dialects/sqlite
)vardb *gorm.DBvarerr errortypePersonstruct{
IDuint`json:id`FirstNamestring`json:firstname`LastNamestring`json:lastname`}funcmain(){//NOTE:See we’re using = to assign the global var// instead of := which would assign it only in this functiondb, err = gorm.Open(sqlite3, ./gorm.db)iferr !=nil{
fmt.Println(err)
}deferdb.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET(/people/, GetPeople)
r.GET(/people/:id, GetPerson)
r.POST(/people, CreatePerson)
r.Run(:8080)
}funcCreatePerson(c *gin.Context){varperson Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}funcGetPerson(c *gin.Context){
id := c.Params.ByName(id)varperson Personiferr := db.Where(id = ?, id).First(&person).Error; err !=nil{
c.AbortWithStatus(404)
fmt.Println(err)
}else{
c.JSON(200, person)
}
}funcGetPeople(c *gin.Context){varpeople []Personiferr := db.Find(&people).Error; err !=nil{
c.AbortWithStatus(404)
fmt.Println(err)
}else{
c.JSON(200, people)
}
}
现在要测试这一点,我们将从命令行运行curl命令。我们还需要服务器运行,因此打开另一个终端窗口,以便您可以运行这两个命令。使用$ go在第一个窗口中运行服务器main.go
一旦运行,在第二个窗口中运行:
$ curl -i -X POSThttp://localhost:8080/people-d ‘{ FirstName: Elvis, LastName: Presley}’
您应该会看到成功的回复
HTTP/1.1200OKContent-Type:application/json;charset=utf-8Date:Sat,03Dec201600:14:06GMTContent-Length:50{id:2,firstname:Elvis,lastname:Presley}
现在,让我们在浏览器中列出人员,以查看它列出了我们所有的条目http:// localhost:8080 / people /
[{id:1,firstname: John,lastname: Doe},{id:2,firstname: Elvis,lastname: Presley}]
太棒了,它有效!您认为这很酷。
这次仅发送部分人
$ curl -i -X POSThttp://localhost:8080/people-d ‘{ FirstName: Madison}’
刷新浏览器,发现它仅添加了我们发送的数据
[{{id}:1, firstname: John, lastname: Doe},{id:2, firstname: Elvis, lastname: Presley},{id:3, firstname: Madison, lastname:}} ]
这是Gin的一部分,请注意CreatePerson函数中的c.BindJSON(&person)行。它会自动从请求中填充所有匹配的数据字段。
另外,您可能已经错过了它,但是我的数据库中的情况与我传入的情况不同。杜松子酒对田野的情况也非常宽容。我传入了名字,但数据库使用了名字。
很简单!
更新
但是,我们不能不给麦迪逊一个姓氏。是时候添加我们的更新功能了
package mainimport(
fmt
github.com/gin-gonic/gin
github.com/jinzhu/gorm_github.com/jinzhu/gorm/dialects/sqlite
)vardb *gorm.DBvarerr error
typePersonstruct{IDuint `json:id`FirstNamestring `json:firstname`LastNamestring `json:lastname`
}funcmain(){//NOTE:See we’re using = to assign the global var// instead of := which would assign it only in this functiondb, err = gorm.Open(sqlite3, ./gorm.db)iferr !=nil{
fmt.Println(err)
}deferdb.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)
}funcUpdatePerson(c*gin.Context){varpersonPersonid :=c.Params.ByName(id)iferr := 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)
}funcCreatePerson(c*gin.Context){varpersonPersonc.BindJSON(&person)
db.Create(&person)c.JSON(200, person)
}funcGetPerson(c*gin.Context){
id :=c.Params.ByName(id)varpersonPersoniferr := db.Where(id = ?, id).First(&person).Error; err !=nil{c.AbortWithStatus(404)
fmt.Println(err)
}else{c.JSON(200, person)
}
}funcGetPeople(c*gin.Context){varpeople []Personiferr := db.Find(&people).Error; err !=nil{c.AbortWithStatus(404)
fmt.Println(err)
}else{c.JSON(200, people)
}
}
为了测试这一点,我们将使用类似的curl命令,但是我们将在特定用户上调用PUT方法
$curl-i-XPUThttp://localhost:8080/people/3-d‘{FirstName:Madison,LastName:Sawyer}’HTTP/1.1200OKContent-Type:application/json;charset=utf-8Date:Sat,03Dec201600:25:35GMTContent-Length:51{id:3,firstname:Madison,lastname:Sawyer}
当然,如果刷新浏览器,我们会看到它在姓氏中添加了sawyer。
[{{id}:1, firstname: John, lastname: Doe},{id:2, firstname: Elvis, lastname: Presley},{id:3, firstname: Madison, lastname: Sawyer} ]
同样,我们可以发送部分数据以进行部分更新
$ curl -i -X PUThttp://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}]
删除
现在,为了完善该解决方案,请让dd进入delete函数。
package mainimport(
fmt
github.com/gin-gonic/gin
github.com/jinzhu/gorm_github.com/jinzhu/gorm/dialects/sqlite
)vardb *gorm.DBvarerr error
typePersonstruct{IDuint `json:id`FirstNamestring `json:firstname`LastNamestring `json:lastname`
}funcmain(){//NOTE:See we’re using = to assign the global var// instead of := which would assign it only in this functiondb, err = gorm.Open(sqlite3, ./gorm.db)iferr !=nil{
fmt.Println(err)
}deferdb.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)
}funcDeletePerson(c*gin.Context){
id :=c.Params.ByName(id)varpersonPersond := db.Where(id = ?, id).Delete(&person)
fmt.Println(d)c.JSON(200, gin.H{id + id: deleted})
}funcUpdatePerson(c*gin.Context){varpersonPersonid :=c.Params.ByName(id)iferr := 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)
}funcCreatePerson(c*gin.Context){varpersonPersonc.BindJSON(&person)
db.Create(&person)c.JSON(200, person)
}funcGetPerson(c*gin.Context){
id :=c.Params.ByName(id)varpersonPersoniferr := db.Where(id = ?, id).First(&person).Error; err !=nil{c.AbortWithStatus(404)
fmt.Println(err)
}else{c.JSON(200, person)
}
}funcGetPeople(c*gin.Context){varpeople []Personiferr := db.Find(&people).Error; err !=nil{c.AbortWithStatus(404)
fmt.Println(err)
}else{c.JSON(200, people)
}
}
为了测试这一点,我们将使用带有curl的Delete方法来调用它
$curl-i-XDELETEhttp://localhost:8080/people/1HTTP/1.1200OKContent-Type:application/json;charset=utf-8Date:Sat,03Dec201600:32:40GMTContent-Length:20{id1:deleted}
刷新浏览器,您将看到我们的John Doe已被删除
[{id:2,firstname: Elvis,lastname: Presley},{id:3,firstname: Tom,lastname: Sawyer}]
改变模型
定义了基本的API之后,现在是开始更改Person对象的好时机。我们只需更改person结构即可轻松修改数据库和api。
我要做的就是在Person Struct中添加一个city字段。没有其他的。一条线。
typePersonstruct{
IDuint`json:id`FirstNamestring`json:firstname`LastNamestring`json:lastname`Citystring`json:city`}
刷新我们的浏览器并拉出一个列表,您可以看到我所有的对象现在都有城市
[{id:2,firstname: Elvis,lastname: Presley,city: },{id:3,firstname: Tom,lastname: Sawyer,city: }]
我可以在没有其他更改的情况下创建和更新新字段
$curl-i-XPUThttp://localhost:8080/people/2-d‘{city:Memphis}’HTTP/1.1200OKContent-Type:application/json;charset=utf-8Date:Sat,03Dec201600:40:57GMTContent-Length:67{id:2,firstname:Elvis,lastname:Presley,city:Memphis}
所有这些都由我们主要函数中的db.AutoMigrate(&Person {})行处理。在生产环境中,我们希望更紧密地管理模式,但是对于原型来说,这是完美的。
使用MySql
我听到了。很好,但是使用MySql代替SQLite怎么办。
为此,我们只需要换出一个import语句和连接。
Import_ 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 referenceimport(
fmt
github.com/gin-gonic/gin_github.com/go-sql-driver/mysql
github.com/jinzhu/gorm_github.com/jinzhu/gorm/dialects/sqlite
)vardb *gorm.DBvarerr error
typePersonstruct{IDuint `json:id`FirstNamestring `json:firstname`LastNamestring `json:lastname`Citystring `json:city`
}funcmain(){//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)iferr !=nil{
fmt.Println(err)
}deferdb.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)
}funcDeletePerson(c*gin.Context){
id :=c.Params.ByName(id)varpersonPersond := db.Where(id = ?, id).Delete(&person)
fmt.Println(d)c.JSON(200, gin.H{id + id: deleted})
}funcUpdatePerson(c*gin.Context){varpersonPersonid :=c.Params.ByName(id)iferr := 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)
}funcCreatePerson(c*gin.Context){varpersonPersonc.BindJSON(&person)
db.Create(&person)c.JSON(200, person)
}funcGetPerson(c*gin.Context){
id :=c.Params.ByName(id)varpersonPersoniferr := db.Where(id = ?, id).First(&person).Error; err !=nil{c.AbortWithStatus(404)
fmt.Println(err)
}else{c.JSON(200, person)
}
}funcGetPeople(c*gin.Context){varpeople []Personiferr := db.Find(&people).Error; err !=nil{c.AbortWithStatus(404)
fmt.Println(err)
}else{c.JSON(200, people)
}
}
结论
Go是一种灵活的语言,具有强大的环境。它非常容易构建,并且只需少量代码即可快速构建功能丰富的应用程序。我希望这是一个有用的演练。请随时评论您的想法和问题
原文:https://medium.com/@cgrant/developing-a-simple-crud-api-with-go-gin-and-gorm-df87d98e6ed1
原创文章 使用Go,Gin和Gorm开发一个简单的CRUDAPI,版权所有
如若转载,请注明出处:https://www.itxiaozhan.cn/202211739.html