gin框架学习-Gin框架和Gorm框架搭建一个简单的API微服务
Posted lin钟一
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gin框架学习-Gin框架和Gorm框架搭建一个简单的API微服务相关的知识,希望对你有一定的参考价值。
目录
前言
感谢开源项目gin-vue-admin,以及1010工作室的视频教程
本人学识尚浅,如有错误,请评论指出,谢谢!
详细可见个人博客:https://linzyblog.netlify.app/
本章会用到的技术,不太熟悉的可以去看看我以前的文章
- 快速安装gin框架:https://blog.csdn.net/weixin_46618592/article/details/125540869
- HTTP请求:https://blog.csdn.net/weixin_46618592/article/details/125565789
- Gorm入门指南:https://blog.csdn.net/weixin_46618592/article/details/125647217
- GORM框架进阶之CRUD接口(数据库增删改查操作):https://blog.csdn.net/weixin_46618592/article/details/125740498
一、设计思路
这章动手把前面学过的Gin框架内容和Gorm框架结合做一个简单的API 微服务
我们需要通过前端来完成对数据库数据的增删改查即可,需要如下这样的 API :
- POST todos/ 添加数据
- GET todos/ 获取数据库所有数据
- GET todos/id 获取指定 id 的数据
- PUT todos/id 修改指定 id 的数据
- DELETE todos/id 删除指定 id 的数据
二、编写后端请求
创建一个 todos 文件夹,用VSCode打开当前文件夹
1、在Terminal终端输入 go mod
go mod init todo
创建包管理工具,此命令会在当前目录中初始化和创建一个新的go.mod文件,手动创建go.mod文件再包含一些module声明也等同该命令,而go mod init命令便是帮我们简便操作,可以帮助我们自动创建。
2、下载并安装所需要的包
go get -u github.com/gin-gonic/gin
go get -u gorm.io/driver/mysql
go get -u gorm.io/gorm
3、创建路由
在 todo 文件夹下创建一个名为 main.go 的文件,在 main 文件中创建一个类似下面的路由,将同一个功能模块放到同一个路由分组中,代码更美观易懂
package main
import (
"github.com/gin-gonic/gin"
)
func main()
router := gin.Default()
v1 := router.Group("/api/v1/todos")
v1.POST("/", createTodo)
v1.GET("/", fetchAllTodo)
v1.GET("/:id", fetchSingleTodo)
v1.PUT("/:id", updateTodo)
v1.DELETE("/:id", deleteTodo)
router.Run()
4、数据库连接
现在我们要实现数据库连接就要用到GORM框架,GORM框架是go的一个数据库连接及交互框架,主要是把struct类型和数据库记录进行映射,数据库语句复杂的情况下可以直接手写语句,一般用于连接关系型数据库,这里我主要使用MySQL数据库。
1)设计数据表
//数据库字段
type todoModel struct
gorm.Model
Title string `json:"title"`
Completed int `json:"completed"`
//处理返回字段
type transformedTodo struct
ID int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
2)数据库连接
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var db *gorm.DB
func init()
dsn := "root:123456@tcp(127.0.0.1:3306)/go_test?charset=utf8&parseTime=True"
var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config)
if err != nil
panic(err.Error())
//自动创建数据表
db.AutoMigrate(&todoModel)
三、实现路由方法
1、创建一条记录
使用到了 Gin 框架中的 Context 上下文来接收 POST 方式传过来的参数,用 Gorm框架 连接数据库使用Save方法或者Create方法都可以来保存相关的数据到数据库,之后会给前端用户返回一个 JSON信息。
//创建一条todo记录
func createTodo(c *gin.Context)
completed, err := strconv.Atoi(c.PostForm("completed"))
if err != nil
panic(err.Error())
todo := todoModel
Title: c.PostForm("title"),
Completed: completed,
db.Save(&todo)
c.JSON(http.StatusCreated, gin.H
"status": http.StatusCreated,
"message": "Todo item created successfully",
"resourceId": todo.ID,
)
- POST请求
- 数据库
2、查询所有记录
使用到了 Gin 框架中的 GET 请求,没有参数,用 Gorm 框架连接数据库使用Find方法查询所有记录,封装在JSON里返回给前端用户
//获取数据库所有todo
func selectAllTodo(c *gin.Context)
var todos []todoModel
var rq_todos []transformedTodo
db.Find(&todos)
if len(todos) <= 0
c.JSON(http.StatusNotFound, gin.H
"status": http.StatusNotFound,
"message": "No todo found!",
)
return
for _, item := range todos
completed := false
if item.Completed == 1
completed = true
rq_todos = append(rq_todos, transformedTodo
ID: int(item.ID),
Title: item.Title,
Completed: completed,
)
c.JSON(http.StatusOK, gin.H
"status": http.StatusOK,
"data": rq_todos,
)
- GET请求
- 数据库
3、查找指定id的记录
使用到了 Gin 框架中的 Context 上下文来接收 GET 方式存放在URI里传过来的参数,用 Gorm 框架连接数据库使用Find方法或者First方法查询指定记录,封装在JSON里返回给前端用户
//查找指定todo
func selectSingleTodo(c *gin.Context)
var todo todoModel
todoID := c.Param("id")
db.First(&todo, todoID)
if todo.ID == 0
c.JSON(http.StatusNotFound, gin.H
"status": http.StatusNotFound,
"message": "No todo found!",
)
return
completed := false
if todo.Completed == 1
completed = true
rp_todo := transformedTodo
ID: int(todo.ID),
Title: todo.Title,
Completed: completed,
c.JSON(http.StatusOK, gin.H
"status": http.StatusOK,
"data": rp_todo,
)
- GET请求
4、修改指定id记录
使用到了 Gin 框架中的 Context 上下文来接收 PUT 方式传过来的参数,用 Gorm 框架连接数据库使用Find方法或者First方法查询指定记录,然后通过Update方法修改记录,返回给前端用户修改成功信息。
//修改指定id todo
func updateTodo(c *gin.Context)
var todo todoModel
todoID := c.Param("id")
db.First(&todo, todoID)
if todo.ID == 0
c.JSON(http.StatusNotFound, gin.H
"status": http.StatusNotFound,
"message": "No todo found!",
)
return
completed, _ := strconv.Atoi(c.PostForm("completed"))
db.Model(&todo).Updates(&todoModel
Title: c.PostForm("title"),
Completed: completed,
)
c.JSON(http.StatusOK, gin.H
"status": http.StatusOK,
"message": "Todo updated successfully",
)
- PUT请求
- 数据库
5、删除指定id记录
使用到了 Gin 框架中的 Context 上下文来接收 DELETE 方式存放在URI里传过来的参数,用 Gorm 框架连接数据库使用First方法或者Find查询指定id记录,用Delete删除指定id记录,返回给前端用户删除成功信息。
//删除指定id todo
func deleteTodo(c *gin.Context)
var todo todoModel
todoID := c.Param("id")
db.First(&todo, todoID)
if todo.ID == 0
c.JSON(http.StatusNotFound, gin.H
"status": http.StatusNotFound,
"message": "No todo found!",
)
return
db.Delete(&todo)
c.JSON(http.StatusOK, gin.H
"status": http.StatusOK,
"message": "todo Deleted successfully",
)
- DELETE请求
- 数据库
四、结论
这章只是把前面的基础的知识Gin框架和Gorm框架结合一起实现,日志、JWT认证和Casbin框架我还没用到,后面我将会学习gva框架把之前所学全部展现并结合出来
如何使用 Gin 和 Gorm 搭建一个简单的 API 服务
创建 API
我们之前已经跑过 Gin 框架的代码,现在是时候加些功能进去了。
读取全部信息
我们先从"增删改查"中的"查"入手,查询我们之前添加的信息。我接下来要删除几行代码,并把 Gin 的框架代码加回来。
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("g/", GetProjects)
r.Run("g: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)
}
}
那么运行程序,并在浏览器中访问 http://localhost:8080,你应该看到:
[{“id”: 1,”firstname”: “John”,”lastname”: “Doe”}]
喔,几行代码我们就可以拿到 API 服务器的响应了,而且大部分代码都是用来错误处理的。
读取特定信息
好,为了把 API 接口写的更符合 REST 规范,我们加入查询特定信息的借口
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("g/", GetProjects)
r.GET("/people/:id", GetPerson)
r.Run("g: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)
}
}
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)
}
}
现在运行程序,但请注意,如果要访问全部信息,你需要访问的地址变成了 http://localhost:8080/people/ ,如果 在 URL 的末尾加入了 ID,你就会得到特定的信息 http://localhost:8080/people/1
{"id": 1, "firstname": "John", "lastname": "Doe"}
添加信息
只有一条记录是看不大出来查询全部信息和查询单条信息的区别的,所以咱们来把添加信息的功能加上吧。
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("g/", GetProjects)
r.GET("/people/:id", GetPerson)
r.POST("/people", CreatePerson)
r.Run("g: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)
}
}
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 CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
接下来让我们从终端运行 curl 命令测试一下新加的功能是不是可用,当然还是先要把程序运行起来。
在终端运行:
$ curl -i -X POST http://localhost:8080/people -d ‘{ "FirstName": "Elvis", "LastName": "Presley"}‘
应该会看到成功的响应消息:
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"}
现在我们访问一下查询全部信息的接口,http://localhost:8080/people/
[{"id": 1,"firstname": "John","lastname": "Doe"},{"id": 2,"firstname": "Elvis","lastname": "Presley"}]
太棒啦,代码没问题。这回我们只发送 Person 结构体的部分信息,看看程序会如何处理。
$ curl -i -X POST http://localhost:8080/people -d ‘{ "FirstName": "Madison"}‘
刷新一下浏览器,发现只添加了我们发送的信息。
[{"id": 1,"firstname": "John","lastname": "Doe"},{"id": 2,"firstname": "Elvis","lastname": "Presley"},{"id": 3,"firstname": "Madison","lastname": ""}]
这就是 Gin 如何工作的了,留意一下 c.BindJSON(&person)
这行,它会自动匹配请求消息中的数据信息。
虽然请求消息里可能缺某些信息,就比如刚才那个例子,而且大小写不匹配也没有关系,Gin 的容错性非常高。非常简单!
更新信息
我们不能把 Madison 这条记录没有姓氏啊,是时候加入更新功能了。
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("g/", GetProjects)
r.GET("/people/:id", GetPerson)
r.POST("/people", CreatePerson)
r.PUT("/people/:id", UpdatePerson)
r.Run("g: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)
}
}
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 CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
func UpdatePerson(c *gin.Context) {
var person Person
id