参考:How to create a basic RESTful API in Go
像任何语言一样,可以使用Golang快速编写基本的RESTful API。在此示例中,数据可以通过JSON格式的标准HTTP方法(GET,POST,PUT&DELETE)访问。数据行将存储在SQLite数据库中。
我们本文演示一个简单的用户管理 RESTFul API
基本框架
路由:
- POST : http://127.0.0.1:8080/api/v1/users
- GET : http://127.0.0.1:8080/api/v1/users
- GET : http://127.0.0.1:8080/api/users/1
- PUT : http://127.0.0.1:8080/api/users/1
- DELETE : http://127.0.0.1:8080/api/users/1
数据表:
- id — integer & auto-increment
- firstname — varchar(255)
- lastname — varchar(255)
使用Gin做路由转发
我们将使用Gin,这是一个微框架路由。它易于使用并且路由性能很牛x。
首先是获取gin包
go get github.com/gin-gonic/gin
好的,我们开始编码!首先在一个新的“main.go”文件中,我们称之为我们的库。
package main import ( "strconv" "github.com/gin-gonic/gin" )
其次,我们声明User结构:
type Users struct { Id int `gorm:"AUTO_INCREMENT" form:"id" json:"id"` Firstname string `gorm:"not null" form:"firstname" json:"firstname"` Lastname string `gorm:"not null" form:"lastname" json:"lastname"` }
“gorm”参数将在以后使用数据库连接时用到...
最后,在main()函数中,我们在一个组中调用我们的路由:
func main() { r := gin.Default() v1 := r.Group("api/v1") { v1.POST("/users", PostUser) v1.GET("/users", GetUsers) v1.GET("/users/:id", GetUser) v1.PUT("/users/:id", UpdateUser) v1.DELETE("/users/:id", DeleteUser) } r.Run(":8080") }
声明完路由,接下来我们要做的就是完成路由对应的处理函数:
func PostUser(c *gin.Context) { // The futur code… } func GetUsers(c *gin.Context) { var users = []Users{ Users{Id: 1, Firstname: "Oliver", Lastname: "Queen"}, Users{Id: 2, Firstname: "Malcom", Lastname: "Merlyn"}, } c.JSON(200, users) // curl -i http://localhost:8080/api/v1/users } func GetUser(c *gin.Context) { id := c.Params.ByName("id") user_id, _ := strconv.ParseInt(id, 0, 64) if user_id == 1 { content := gin.H{"id": user_id, "firstname": "Oliver", "lastname": "Queen"} c.JSON(200, content) } else if user_id == 2 { content := gin.H{"id": user_id, "firstname": "Malcom", "lastname": "Merlyn"} c.JSON(200, content) } else { content := gin.H{"error": "user with id#" + id + " not found"} c.JSON(404, content) } // curl -i http://localhost:8080/api/v1/users/1 } func UpdateUser(c *gin.Context) { // The futur code… } func DeleteUser(c *gin.Context) { // The futur code… }
写到这里,我们的API服务器已经可以使用了,我们运行起来:
go run main.go
正如你所看到的,为了显示用户,我们采用GET方式获取User列表,返回假的数据:
[{"id":1,"firstname":"Oliver","lastname":"Queen"},{"id":2,"firstname":"Malcom","lastname":"Merlyn"}]
如果只是查找某个用户:
{"firstname":"Oliver","id":1,"lastname":"Queen"}
将数据存入到SQLite
go get github.com/jinzhu/gorm
使用Gorm,我们将使用一个SQLite数据库。您也可以使用MySQL(和MariaDB),Postgres和FoundationDB数据库。
go get github.com/mattn/go-sqlite3
如果您忘记导入新的库文件(并且我们不需要“strconv”库),您的编译器将会报错。
import ( "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" _ "github.com/mattn/go-sqlite3" )
不需要手动创建数据库文件和用户表,Gorm可以帮你实现。 ☺
func InitDb() *gorm.DB { // Openning file db, err := gorm.Open("sqlite3", "./data.db") db.LogMode(true) // Error if err != nil { panic(err) } // Creating the table if !db.HasTable(&Users{}) { db.CreateTable(&Users{}) db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&Users{}) } return db }
在下次重新启动您的服务器时,我们将会使用data.db数据库,并且根据User字段创建好了User表。
那么下面就讲SQLite针对User的CRUD操作。
新增用户
func PostUser(c *gin.Context) { db := InitDb() defer db.Close() var user Users c.Bind(&user) if user.Firstname != "" && user.Lastname != "" { // INSERT INTO "users" (name) VALUES (user.Name); db.Create(&user) // Display error c.JSON(201, gin.H{"success": user}) } else { // Display error c.JSON(422, gin.H{"error": "Fields are empty"}) } // curl -i -X POST -H "Content-Type: application/json" -d "{ \"firstname\": \"Thea\", \"lastname\": \"Queen\" }" http://localhost:8080/api/v1/users }
在curl命令之后,data.db被创建,然后返回结果如下:
HTTP/1.1 201 Created Content-Type: application/json; charset=utf-8 {"success":{"id":1,"firstname":"Thea","lastname":"Queen"}}
获取所有用户
func GetUsers(c *gin.Context) { // Connection to the database db := InitDb() // Close connection database defer db.Close() var users []Users // SELECT * FROM users db.Find(&users) // Display JSON result c.JSON(200, users) // curl -i http://localhost:8080/api/v1/users }
获取结果:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 [{"id":1,"firstname":"Thea","lastname":"Queen"}]
获取指定User
func GetUser(c *gin.Context) { // Connection to the database db := InitDb() // Close connection database defer db.Close() id := c.Params.ByName("id") var user Users // SELECT * FROM users WHERE id = 1; db.First(&user, id) if user.Id != 0 { // Display JSON result c.JSON(200, user) } else { // Display JSON error c.JSON(404, gin.H{"error": "User not found"}) } // curl -i http://localhost:8080/api/v1/users/1 }
返回结果:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 {"id":1,"firstname":"Thea","lastname":"Queen"}
更新用户
func UpdateUser(c *gin.Context) { // Connection to the database db := InitDb() // Close connection database defer db.Close() // Get id user id := c.Params.ByName("id") var user Users // SELECT * FROM users WHERE id = 1; db.First(&user, id) if user.Firstname != "" && user.Lastname != "" { if user.Id != 0 { var newUser Users c.Bind(&newUser) result := Users{ Id: user.Id, Firstname: newUser.Firstname, Lastname: newUser.Lastname, } // UPDATE users SET firstname='newUser.Firstname', lastname='newUser.Lastname' WHERE id = user.Id; db.Save(&result) // Display modified data in JSON message "success" c.JSON(200, gin.H{"success": result}) } else { // Display JSON error c.JSON(404, gin.H{"error": "User not found"}) } } else { // Display JSON error c.JSON(422, gin.H{"error": "Fields are empty"}) } // curl -i -X PUT -H "Content-Type: application/json" -d "{ \"firstname\": \"Thea\", \"lastname\": \"Merlyn\" }" http://localhost:8080/api/v1/users/1 }
返回结果:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 {"success":{"id":1,"firstname":"Thea","lastname":"Merlyn"}}
删除用户
func DeleteUser(c *gin.Context) { // Connection to the database db := InitDb() // Close connection database defer db.Close() // Get id user id := c.Params.ByName("id") var user Users // SELECT * FROM users WHERE id = 1; db.First(&user, id) if user.Id != 0 { // DELETE FROM users WHERE id = user.Id db.Delete(&user) // Display JSON result c.JSON(200, gin.H{"success": "User #" + id + " deleted"}) } else { // Display JSON error c.JSON(404, gin.H{"error": "User not found"}) } // curl -i -X DELETE http://localhost:8080/api/v1/users/1 }
返回结果:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 {"success":"User #1 deleted"}
CORS
您可以直接在路由中使用CORS。
c.Writer.Header().Add("Access-Control-Allow-Origin", "*") c.Next()
或者在全球范围内使用“gin.HandlerFunc”功能的自定义中间件:
func Cors() gin.HandlerFunc { return func(c *gin.Context) { c.Writer.Header().Add("Access-Control-Allow-Origin", "*") c.Next() } }
在路由调用之前传入该中间件:
r.Use(Cors())
如果您没有激活CORS,Chrome将会收到与此类似的消息错误:
XMLHttpRequest cannot load http://localhost:8080/api/v1/users. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:8081' is therefore not allowed access.
AJAX OPTIONS
如果您使用Javacript和CORS使用“XMLHttpRequest”或“Fetch”,则需要使用“OPTIONS”作为POST,PUT,DELETE请求。
首先,您必须添加2条路由。
v1.OPTIONS("/users", OptionsUser) // POST v1.OPTIONS("/users/:id", OptionsUser) // PUT, DELETE
并声明“OptionsUser”函数:
func OptionsUser(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Methods", "DELETE,POST, PUT") c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type") c.Next() }
如果您不使用此方法,您将会遇到与Chrome相似的消息错误:
XMLHttpRequest cannot load http://localhost:8080/api/v1/users/1. Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:8081' is therefore not allowed access. The response had HTTP status code 404.
事实上,浏览器找不到该OPTIONS链接:
http://localhost:8080/api/v1/users/1
总结
按照上述代码我们便实现了一个简单具有基本CRUD功能的RESTful API的例子。你可以看到,数据结构体在Golang中是非常重要的,特别是当你操纵一些Json数据时。
源码下载地址:Golang Gin RESTFul API with SQLite
更多参考信息:
- Gin : https://github.com/gin-gonic/gin
- Gorm : http://jinzhu.me/gorm
- Go SQLITE 3 driver :https://github.com/mattn/go-sqlite3
- Go Sublime (a Sublime Text plugin) : https://github.com/DisposaBoy/GoSublime
- Code available : https://gist.github.com/EtienneR/ed522e3d31bc69a9dec3335e639fcf60
- Old Code still available (with Gorp and MySQL) : https://gist.github.com/EtienneR/5eb48ae7d849cec6f55a
文章的脚注信息由WordPress的wp-posturl插件自动生成