前言

本文通过一步一步的设计,最终实现一个完善的todo应用。

我们使用GO框架Gin用户路由控制和返回数据。使用Gorm用于操作数据库。

读者可根据本教程操作,最终实现列出的各项功能。

技术清单

本文中所涉及的技术内容主要有以下几种:Gin:轻量高效性能爆棚的WEB框架

Gorm:一个关系型数据库的ORM工具包,避免直接SQL语句操作

MySQL:数据库

curl工具,用于API接口数据测试

另外,使用的GO版本是go version go1.13.5 windows/amd64

对于第2,3条内容,可使用以下指令安装

对于工具curl,我们使用的是 git bash内自带的指令。如果是linux下用户,开箱即用。

创建数据库

本文使用MySQL数据库装载数据。本节我们仅需创建一个空的数据库,就可以了。表结构在下一节使用gorm迁移功能创建。

使用Navicat工具新建界面如下图。

需要特别留意数据库字符集编码使用 utf8mb4,这个是MySQL真正的utf8,用于中文字符支持。

创建表模型

gorm中的Automigrate()操作,用于刷新数据库中的表,使其保持最新。即让数据库之前存储的记录的表字段和程序中最新使用的表字段保持一致(只增不减)。

我们先建一个todos表模型。

type (

todoModel struct {

gorm.Model

Title string `json:"title"`

Completed int `json:"completed`

}

fmtTodo struct {

ID uint `json:"id"`

Title string `json:"title"`

Completed bool `json:"completed"`

}

)

其中 todoModel用于数据库todos表。我们默认继承使用了gorm.Model的字段,主要包括以下几个:

// gorm.Model 定义

type Model struct {

ID uint `gorm:"primary_key"`

CreatedAt time.Time

UpdatedAt time.Time

DeletedAt *time.Time

}

数据库表会自动生成上述4个字段,并追加 title,completed两个字段。模型名与表名不一致,我们手动指定表名:

// 指定表名

func (todoModel) TableName() string {

return "todos"

}

然后在代码初始化过程中执行迁移。

var db *gorm.DB

// 初始化

func init() {

var err error

var constr string

constr = fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", "root", "111213", "localhost", 3306, "05-gin-gorm-todo")

db, err = gorm.Open("mysql", constr)

if err != nil {

panic("数据库连接失败")

}

db.AutoMigrate(&todoModel{})

}

首先声明一个 gorm.DB,用于数据库操作。

在使用gorm包之前,需要导入。

import (

"github.com/jinzhu/gorm"

_ "github.com/jinzhu/gorm/dialects/mysql"

)

第二项我们仅导入而不使用。这个导入操作,gorm执行了下述操作

import _ "github.com/go-sql-driver/mysql"

这样我们无需重新手动处理包依赖关系。

MySQL的连接字符串是有特定格式的,这也是由底层 go-sql-driver/mysql 决定的参数配置项。其含义如下:

user:password@(hostname:port)/database_name?charset=utf8mb4&parseTime=True&loc=Local

此处我们使用的本地数据库3306端口,root用户,和 05-gin-gorm-todo 数据库。

在执行完整的程序之后,todos表会被自动迁移创建,其详细参数如下图。

这张表也是我们本文所操作的数据基础。

规划路由

依照restful风格的API设计标准,我们规划了5个路由,涵盖了一个todo列表清单的增删改查功能。

func main() {

r := gin.Default()

v1 := r.Group("/api/v1/todo")

{

v1.POST("/", add) // 添加新条目

v1.GET("/", all) // 查询所有条目

v1.GET("/:id", take) // 获取单个条目

v1.PUT("/:id", update) // 更新单个条目

v1.DELETE("/:id", del) // 删除单个条目

}

r.Run(":9089")

}

这个是最终会使用到的main函数,使用了gin提供的路由功能。为了扩展方便,我们使用了gin路由的Group功能,将版本v1的所有路由集中处理。

其中,访问的方法跟别使用 POST表示添加,GET表示查询,PUT表是更新,DELETE表示删除,这是restful API设计的一般性方法。

API功能

上一节规划的路由中,我们声明了5个函数,本节逐一实现这5个函数。注意API返回数据都是JSON格式。

为了统一返回状态码,对于正确响应的,返回HTTP CODE = 200。JSON数据的正常与否,使用两个常量表示,如下:

const (

JSON_SUCCESS int = 1

JSON_ERROR int = 0

)

成功返回1,失败返回0。

1 - 添加条目 add

主要的功能,是拿到POST表单提交的数据,并写入数据库,成功则返回信息通知,失败则给出相应提示。

// 创建TODO条目func add(c *gin.Context) {

completed, _ := strconv.Atoi(c.PostForm("completed"))

todo := todoModel{Title: c.PostForm("title"), Completed: completed}

db.Save(&todo)

c.JSON(http.StatusOK, gin.H{

"status": JSON_SUCCESS,

"message": "创建成功",

"resourceId": todo.ID,

})

}

注意我们对于POST提供的字段 completed 进行了转换,因为表单数据默认都是字符串类型,而todoModel内Completed是int类型,所以需要进行转换。

2 - 获取所有条目

不接受任何参数,默认给出所有的条目内容。

// 获取所有条目func all(c *gin.Context) {

var todos []todoModel

var _todos []fmtTodo

db.Find(&todos)

// 没有数据if len(todos) <= 0 {

c.JSON(http.StatusOK, gin.H{

"status": JSON_ERROR,

"message": "没有数据",

})

return

}

// 格式化for _, item := range todos {

completed := false

if item.Completed == 1 {

completed = true

} else {

completed = false

}

_todos = append(_todos, fmtTodo{

ID: item.ID,

Title: item.Title,

Completed: completed,

})

}

c.JSON(http.StatusOK, gin.H{

"status": JSON_SUCCESS,

"message": "ok",

"data": _todos,

})

}

为了API返回数据的便于使用,我们使用了一个结构体 fmtTodo 用于将结构体 todoModel 的数据进行格式化。如果没有查询到任何数据,返回状态码 status = 0。

3 - 获取单个条目

在路由中附加的id,可以调用此路由,用于返回单条数据。

// 根据id获取一个条目func take(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": JSON_ERROR,

"message": "条目不存在",

})

return

}

completed := false

if todo.Completed == 1 {

completed = true

} else {

completed = false

}

_todo := fmtTodo{

ID: todo.ID,

Title: todo.Title,

Completed: completed,

}

c.JSON(http.StatusOK, gin.H{

"status": JSON_SUCCESS,

"message": "ok",

"data": _todo,

})

}

根据ID查询数据,如果存在就返回,并使用fmtTodo进行格式化;如果不存在,状态码等于0。

4 - 更新单个条目

已经存在的数据,根据ID对其内容进行修改。如果ID不存在,返回错误信息。

// 更新一个条目func update(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": JSON_ERROR,

"message": "条目不存在",

})

return

}

db.Model(&todo).Update("title", c.PostForm("title"))

completed, _ := strconv.Atoi(c.PostForm("completed"))

db.Model(&todo).Update("completed", completed)

c.JSON(http.StatusOK, gin.H{

"status": JSON_SUCCESS,

"message": "更新成功",

})

}

5 - 删除单个条目

根据ID查询是否存在,如果存在就进行删除。

// 删除条目func del(c *gin.Context) {

var todo todoModel

todoID := c.Param("id")

db.First(&todo, todoID)

if todo.ID == 0 {

c.JSON(http.StatusOK, gin.H{

"status": JSON_ERROR,

"message": "条目不存在",

})

return

}

db.Delete(&todo)

c.JSON(http.StatusOK, gin.H{

"status": JSON_SUCCESS,

"message": "删除成功!",

})

}

以上就是5个方法的具体实现,只能用作demo,而不能用于生产。因为表单数据的有效性检测,我们在代码中并没有实现。这在线上是绝对不允许的。

还有一些数据的鉴权,用户身份权限鉴定,本示例中都没有。

使用curl测试

完成以上步骤,该todo清单功能基本完善,我们使用

go build main.go

进行编译,如果不出错,编译通过后,会生成 main.exe 文件。我们在命令行直接运行该文件,结果如下图。

下面我们对五个url分别进行测试。

首先是获取所有的条目,

curl -s -X GET http://localhost:9089/api/v1/todo/

这会命中第二条路由规则,返回值如下:

{"message":"没有数据","status":0}

暂时没有数据。接着添加一个条目:

curl -s -X POST -d "title=Talk is cheap, show me the code." -d "completed=0" http://localhost:9089/api/v1/todo/

执行成功返回数据

{"message":"创建成功","resourceId":8,"status":1}

接着我们再查询所有的条目,看看能否找到。返回结果如下:

{"data":[{"id":8,"title":"Talk is cheap, show me the code.","completed":false}],"message":"ok","status":1}

为了演示方便,我们再随机写入几条数据,然后测试单条数据查询,修改,和删除。

curl -s -X POST -d "title=Life is short, I use python." -d "completed=0" http://localhost:9089/api/v1/todo/

curl -s -X POST -d "title=Zen of Golang." -d "completed=0" http://localhost:9089/api/v1/todo/

curl -s -X POST -d "title=Do not repeat yourself." -d "completed=0" http://localhost:9089/api/v1/todo/

然后获取所有的条目

curl -s -X GET http://localhost:9089/api/v1/todo/

结果JSON数组格式化输出如下:

测试获取单条数据,这里使用id=10这一条,执行如下指令

curl -s -X GET http://localhost:9089/api/v1/todo/10

返回结果如下:

{"data":{"id":10,"title":"Zen of Golang.","completed":false},"message":"ok","status":1}

测试修改该条数据,设置completed=1,为已完成状态。

curl -s -X PUT -d "title=Zen of Golang." -d "completed=1" http://localhost:9089/api/v1/todo/10

执行成功返回:

{"message":"更新成功!","status":1}

注意更新操作使用的method = PUT。会命中第4条路由规则。

下面删除id=10的条目,使用以下指令:

curl -s -X DELETE http://localhost:9089/api/v1/todo/10

执行成功后返回结果:

{"message":"删除成功!","status":1}

OK,上面所有的路由都已经测试完毕,看服务器端的访问历史,大致如下图:

注意到有一条是数据库连接的自动释放,这是由MySQL设置的连接超时时间决定的,超期闲置则释放。如果有新的连接请求,重新建立。这可以节约资源。

关键点总结

在测试上述功能的时候,列出一些初学者可能会犯的错。

1 - 数据库连接失败

一定要确保连接字符串书写正确,账号密码书写正确,数据库IP地址和端口号正确,还有数据库名称对应。如果始终不能连接成长,可以尝试单独拿出来数据库连接进行测试,直到通过。

2 - 路由地址

根据设定的路由规则,正确地书写路由地址,还有传送参数方法,这样才能在程序中获取到提交的数据。

比如使用POST,传送的表单数据使用 c.PostForm 可以获取到。而 c.Param 则用于获取路由中 “/:id” id 这个位置参数。

3 - curl测试工具使用

注意使用请求方式 -X 参数,还有POST中使用的 -d 参数选项。

结语

以上内容使用两个成熟的包,快速地创建了一个待办清单的微服务。可以看到Go语言生态日臻完善,优秀的框架频出,给开发带来了很高的效率。

另外,GO语言的易于书写特性,接近与脚本语言的表达力,还有严格的数据类型检测,将不少低级的错误排除在编译阶段。

Happy coding :-)

【本文由 @程序员小助手 发布,持续分享编程与程序员成长相关的内容,欢迎关注】

go语言mysql框架_超级详细:Go语言框架Gin和Gorm实现一个完整的待办事项微服务...相关推荐

  1. e语言mysql中文_大佬们E语言连接MYSQL输出中文乱码怎么破

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 .版本 2 .支持库 mysql .支持库 iext .程序集 窗口程序集_启动窗口 .子程序 _按钮1_被单击 数据库连接句柄 = 连接MySql (& ...

  2. 机械优化黄金分割法c语言编程,黄金分割法_机械优化设计_C语言程序.doc

    黄金分割法_机械优化设计_C语言程序黄金分割法_机械优化设计_C语言程序 黄金分割法的优化设计 实验报告 学院:机电工程 机制自动化11-03班 学号:541102010326 姓名:刘点点 1,黄金 ...

  3. r语言平均值显著性检验_最全的R语言统计检验方法_数据挖掘中R语言的运用

    最全的R语言统计检验方法_数据挖掘中R语言的运用 统计检验是将抽样结果和抽样分布相对照而作出判断的工作.主要分5个步骤:建立假设 求抽样分布 选择显著性水平和否定域 计算检验统计量 判定 假设检验(h ...

  4. go语言学习路线图_开篇:Go 语言的优势与学习路线图

    开篇:Go 语言的优势与学习路线图 由 学院君 创建于1年前, 最后更新于 4个月前 版本号 #5 简介 Go 语言又称 Golang,由 Google 公司于 2009 年发布,近几年伴随着云计算. ...

  5. e语言mysql怎么放在超级列表框_易语言中的MYSQL操作之超级列表框使用

    .版本 2 .支持库 mysql .支持库 iext .支持库 CnCalendar .支持库 spec .程序集 窗口程序集_启动窗口 .程序集变量 MySQL句柄, 整数型 .程序集变量 记录集, ...

  6. bilibili go框架_最好的开发语言一定是那位吗?Go语言有话说

    鲁迅先生说过,所有能用 JS 写的前端项目最终都会被用 JS 重写一遍,所有能用 Go 写的后端项目最终也都会被用 Go 重写一遍. 作为一名开发者, 周六的我们能做什么呢? 是因为产品经理的各种需求 ...

  7. e语言mysql怎么放在超级列表框,『易语言怎么将超级列表框的内容保存到excel或者Access!』...

    易语言把编辑框里的内容保存到Excel里 个人建议: 1.建议选据库成你要的修改.添加.查作 2.添加一个列表框,可以轻松直观的处理数据 3.如果有使用Excel的需求,那就弄一个导出按键 这样才能是 ...

  8. 易语言 mysql多线程_易语言数据库多线程 易语言数据库教程

    为什么易语言两个线程同时对一个MYSQL数据? 数据库有自己的连接锁定机制.如果同一台机器使用同一接口插入,则多线程和单线程是相同的. 除非您有多个数据库服务器,然后使用多线程来完成上述工作,否则效率 ...

  9. python异步网络通信框架_超级快的 Python 异步网络框架

    简短介绍 asyncio是遵循Python标准库的一个异步 I/O框架.在这篇文章里,我将介绍 uvloop: 可以完整替代asyncio事件循环.uvloop是用Cython写的,基于 libuv. ...

最新文章

  1. 中国互联网+机器视觉行业商业模式创新与投资机会深度研究报告
  2. 解决Eclipse无法打开“Failed to load the JNI shared library”(转)
  3. 机器学习接口代码之 Ridge、Lasso、Elasitc Net
  4. php5.3升级5.4,centos php 5.3升级到 php5.4版本
  5. nodejs使用supervisor插件调试效率
  6. C#调用java类、jar包方法
  7. Java 图形界面开发--图文并茂建立学生管理系统
  8. 电影视频剪辑怎么做?分享三个小技巧,剪辑其实也不难
  9. 数据禾|甘肃省国家湿地公园功能区划数据
  10. EnPass+WebDAV(一个跨平台密码管理解决方案)
  11. 开始搞点其他的事-成立北京租房群(霍营、回龙观、西二旗、望京)
  12. Axure的动态面板制作tab切换效果
  13. ABAP 从CDS VIEW 发布OData Service示例
  14. 基于JavaSDK实现微信支付,springboot封装签名过程,直接调用controller层实现调起微信支付接口
  15. 烟波流传,繁花三千,倾听似水流年
  16. Catch That Cow
  17. 生活的改变从这一刻开始:香港之旅
  18. js根据后缀名判断文件的类型
  19. 【单片机毕业设计】基于单片机的空气质量(CO、有害混合气体)检测系统的设计
  20. jmeter 计数器_Jmeter之计数器

热门文章

  1. S3C2440 lds链接脚本解析
  2. Golang 词法分析器浅析
  3. android设备报警推送,Firebase推送通知未送达所有android设备
  4. 计算机英语A卷答案,计算机专业英语试题及答案A卷.doc
  5. java 调用 mahout_java – 运行Mahout本地获取MahoutDriver的ClassNotFoundException
  6. 女生做产品经理好吗_女生天生就是产品经理,不服来战!
  7. ceph存储原理_赠书 | Linux 开源存储全栈详解——从Ceph到容器存储
  8. 2019年1月份GitHub上最热门的Java开源项目
  9. Java程序员遇到瓶颈后我们可以试着朝四个方向拓展?你们觉得呢?
  10. 国外计算机专业入门语言,【转自知乎】给想要报考计算机专业学生的一些建议...