(原文作者:Saddam H)
今天,我将使用golang编程语言为一个todo应用构建一个简单的API 。我将使用golang最简单/最快的框架gin-gonic和一个漂亮的ORM gorm来进行数据库工作。要安装这些软件包,请进入工作区$GOPATH/src并在下面运行以下命令:
$ go get go $ go get -u gi $ go get gi
一个通用crud应用程序中,我们需要以下API:
- POST todos/
- GET todos/
- GET todos/{id}
- PUT todos/{id}
- DELETE todos/{id}
让我们开始编码,转到$GOPATH/src并创建一个目录。在todo目录中,创建一个文件main.go。将“gin framework”导入到我们的项目中,并在内部main函数中创建如下所示的路由。我喜欢添加api的前缀,例如“api/v1/”,这就是为什么我们将使用路由器Group方法的原因
包主要 导入( “ gi” )func main(){路由器:= gin.Default()v1:= rou(“ / api / v1 / todos”) { v1.POST(“ /”,createTodo) v1.GET(“ /”,fetchAllTodo) v1.GET(“ /:id”,fetchSingleTodo) v1 .PUT(“ /:id”,updateTodo) v1.DELETE(“ /:id”,deleteTodo) } rou()}
我们已经创建了5条路线,它们处理诸如createTodo, fetchAllTodo etc之类的某些功能。我们将很快对其进行讨论。
现在我们需要建立一个数据库连接。要使用数据库,请在我们的代码中拉gorm包和mysql方言。请遵循以下代码:
包装主要 进口( “gi” “gi” _ “gi/dialects/mysql” ) 变种DB * gorm.DBfunc init(){ //打开数据库连接 var err error db,err = gorm.Open(“ mysql”,“ root:12345 @ / demo?charset = utf8&parseTime = True&loc = Local”) 如果err!= nil { 恐慌(“连接数据库失败”) }//迁移架构 db.AutoMigrate(&todoModel {}) }
在上面的代码中,“ mysql”是我们的数据库驱动程序,“ root”是数据库用户名,“ 12345”密码和“ demo”是数据库名称。请根据需要更改这些信息。
我们将使用数据库功能来获取数据库连接。在让todoModel 和transformedTodo结构体。第一个结构将代表原始的Todo,第二个结构将保留转换后的todo以响应api。在这里,我们转换了todo响应,因为我们没有向使用者公开某些数据库字段(updated_at,created_at)。
type ( // todoModel describes a todoModel type todoModel struct { gorm.Model Title string `json:"title"` Completed int `json:"completed"` } // transformedTodo represents a formatted todo transformedTodo struct { ID uint `json:"id"` Title string `json:"title"` Completed bool `json:"completed"` } )
Todo struct还有一个字段gorm.Model,这意味着什么?好吧,该字段将为我们嵌入一个Model结构,其中包含四个字段“ ID,CreatedAt,UpdatedAt和DeletedAt”
Gorm具有迁移功能,我们已经在init功能中使用了它。当我们首先运行该应用程序时,它将创建一个连接,然后进行迁移。
//Migrate the schema db.AutoMigrate(&todoModel{})
使用phpmyadmin
您还记得我们在一分钟前写的五条路线吗?让我们一一实现五种方法。
当用户将带有“标题和完成的”字段的POST请求发送到路径“api/v1/todos/”时,它将由此路由处理 v1.POST(“/”, createTodo)
让我们实现createTodo函数
// createTodo add a new todo func createTodo(c *gin.Context) { completed, _ := ("completed")) todo := todoModel{Title: c.PostForm("title"), Completed: completed} db.Save(&todo) c.JSON, gin.H{"status": , "message": "Todo item created successfully!", "resourceId": }) }
在上面的代码中,我们使用gin Context接收发布的数据,并使用gorm数据库连接保存待办事项。保存资源后,我们会向resource id用户发送具有良好意义的回复。
让我们实现其余功能
// fetchAllTodo fetch all todos func fetchAllTodo(c *gin.Context) { var todos []todoModel var _todos []transformedTodo db.Find(&todos) if len(todos) <= 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } //transforms the todos for building a good response for _, item := range todos { completed := false if i == 1 { completed = true } else { completed = false } _todos = append(_todos, transformedTodo{ID: i, Title: i, Completed: completed}) } c.JSON, gin.H{"status": , "data": _todos}) } // fetchSingleTodo fetch a single todo func fetchSingleTodo(c *gin.Context) { var todo todoModel todoID := c.Param("id") db.First(&todo, todoID) if == 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } completed := false if == 1 { completed = true } else { completed = false } _todo := transformedTodo{ID: , Title: , Completed: completed} c.JSON, gin.H{"status": , "data": _todo}) } // updateTodo update a todo func updateTodo(c *gin.Context) { var todo todoModel todoID := c.Param("id") db.First(&todo, todoID) if == 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } db.Model(&todo).Update("title", c.PostForm("title")) completed, _ := ("completed")) db.Model(&todo).Update("completed", completed) c.JSON, gin.H{"status": , "message": "Todo updated successfully!"}) } // deleteTodo remove a todo func deleteTodo(c *gin.Context) { var todo todoModel todoID := c.Param("id") db.First(&todo, todoID) if == 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } db.Delete(&todo) c.JSON, gin.H{"status": , "message": "Todo deleted successfully!"}) }
在fetchAllTodo函数中,我们获取了所有待办事项,并使用构造了转换后的响应id, title, completed。我们删除了CreatedAt,UpdatedAt和DeletedAt字段,并将整数值转换为bool。
好吧,我们编写了足够的代码,让我们尝试构建应用程序并对其进行测试,我将使用chrome扩展程序Postman对其进行测试(您可以使用curl等任何REST客户端进行测试)。
要构建应用,请打开您的终端并进入项目目录
$ go build main.go
该命令将生成一个二进制文件,main并使用此命令运行该文件$ ./main。哇,我们简单的待办事项应用程序正在端口8080上运行。它将显示调试日志,因为默认情况下gin以调试模式和端口8080运行。
要测试api,请运行postman并依次测试api
创建一个待办事项
获取所有待办
获取一个待办事项
更新待办事项
删除待办事项
需要完整的源代码吗?
package main import ( "net/http" "strconv" "gi" "gi" _ "gi/dialects/mysql" ) var db *gorm.DB func init() { //open a db connection var err error db, err = gorm.Open("mysql", "root:12345@/demo?charset=utf8&parseTime=True&loc=Local") if err != nil { panic("failed to connect database") } //Migrate the schema db.AutoMigrate(&todoModel{}) } func main() { router := gin.Default() v1 := rou("/api/v1/todos") { v1.POST("/", createTodo) v1.GET("/", fetchAllTodo) v1.GET("/:id", fetchSingleTodo) v1.PUT("/:id", updateTodo) v1.DELETE("/:id", deleteTodo) } rou() } type ( // todoModel describes a todoModel type todoModel struct { gorm.Model Title string `json:"title"` Completed int `json:"completed"` } // transformedTodo represents a formatted todo transformedTodo struct { ID uint `json:"id"` Title string `json:"title"` Completed bool `json:"completed"` } ) // createTodo add a new todo func createTodo(c *gin.Context) { completed, _ := ("completed")) todo := todoModel{Title: c.PostForm("title"), Completed: completed} db.Save(&todo) c.JSON, gin.H{"status": , "message": "Todo item created successfully!", "resourceId": }) } // fetchAllTodo fetch all todos func fetchAllTodo(c *gin.Context) { var todos []todoModel var _todos []transformedTodo db.Find(&todos) if len(todos) <= 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } //transforms the todos for building a good response for _, item := range todos { completed := false if i == 1 { completed = true } else { completed = false } _todos = append(_todos, transformedTodo{ID: i, Title: i, Completed: completed}) } c.JSON, gin.H{"status": , "data": _todos}) } // fetchSingleTodo fetch a single todo func fetchSingleTodo(c *gin.Context) { var todo todoModel todoID := c.Param("id") db.First(&todo, todoID) if == 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } completed := false if == 1 { completed = true } else { completed = false } _todo := transformedTodo{ID: , Title: , Completed: completed} c.JSON, gin.H{"status": , "data": _todo}) } // updateTodo update a todo func updateTodo(c *gin.Context) { var todo todoModel todoID := c.Param("id") db.First(&todo, todoID) if == 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } db.Model(&todo).Update("title", c.PostForm("title")) completed, _ := ("completed")) db.Model(&todo).Update("completed", completed) c.JSON, gin.H{"status": , "message": "Todo updated successfully!"}) } // deleteTodo remove a todo func deleteTodo(c *gin.Context) { var todo todoModel todoID := c.Param("id") db.First(&todo, todoID) if == 0 { c.JSON, gin.H{"status": , "message": "No todo found!"}) return } db.Delete(&todo) c.JSON, gin.H{"status": , "message": "Todo deleted successfully!"}) }
注意:使用代码生产时,必须注意以下步骤:
- 不获取所有数据select * from todos,使用分页
- 不信任用户输入。您必须验证输入,有几种工具可以验证输入。阅读(https://medium.com/@thedevsaddam/an-easy-way-to-validate-go-request-c15182fd11b1)有关验证过程的文章
- 检查所有可能的错误
- 您应该根据需要使用日志记录和身份验证
我为我真的很抱歉BAD 英语和写作流程。如果您发现文章中有任何错误,请随时发表评论。
如果您发现文章有帮助,请在中关注我)
喜欢这篇文章吗?阅读另一篇有趣的文章:如何使用Go(lang)访问深度嵌套的JSON数据?
原文:https://thedevsaddam.medium.com/build-restful-api-service-in-golang-using-gin-gonic-framework-85b1a6e176f3