前言
Gin的优点
无崩溃:Gin可以捕获HTTP请求期间发生的紧急情况并进行恢复。这样,您的服务器将始终可用。
JSON验证:Gin可以解析和验证请求的JSON,例如,检查所需值的存在。
路由分组:更好地组织您的路由分组。需要授权与不需要授权,使用不同的API版本,此外,可以无限嵌套,而不会降低性能。
错误管理:Gin提供了一种方便的方法来收集HTTP请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库中。
内置的渲染:Gin为JSON,XML和HTML渲染提供了易于使用的API。
可扩展:创建新的中间件非常简单。
- Github上Star数量最多
Gin>Beego>Iris
Github上Star数量
Gin 7k
https://github.com/gin-gonic/gin
文档:https://gin-gonic.com/zh-cn/docs/quickstart/
Beego 5.5k
https://github.com/beego/beego
文档:https://beego.gocn.vip/beego/zh/developing/#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B
Iris 2.4k
https://github.com/kataras/iris/
文档:https://www.topgoer.com/Iris/
创建项目
创建项目 z-ad-platform-server
新建main.go
1 | package main |
目录下执行
1 | go mod init z-ad-platform-server |
运行
1 | go run main.go |
访问:http://localhost:8080/hello
跨域
下载
1 | go get github.com/gin-contrib/cors |
引用
1 | import "github.com/gin-contrib/cors" |
设置允许的域
1 | func main() { |
允许所有
1 | func main() { |
路由拆分
main.go
1 | package main |
routers/user_router.go
1 | package routers |
访问:http://localhost:8080/user/info
注意:
对其他包暴露的方法首字母要大写。
路由重定向
HTTP 重定向很容易。 内部、外部重定向均支持。
1 | import "github.com/gin-gonic/gin" |
通过 POST 方法进行 HTTP 重定向。
1 | r.POST("/test", func(c *gin.Context) { |
路由重定向,使用 HandleContext
:
1 | r.GET("/test", func(c *gin.Context) { |
获取请求参数
Query/PostForm
请求
1 | POST /post?id=1234&page=1 HTTP/1.1 |
获取方式
1 | router.POST("/post", func(c *gin.Context) { |
请求体为JSON
方式1
1 | //需要导入包 |
方式2
使用场景 | 函数 |
---|---|
单次绑定 | ShouldBindJSON > BindJSON |
多次绑定 | ShouldBindBodyWith |
单次绑定优选使用ShouldBindJSON
方法,主要因为BindJSON
方法会强制抛出错误,影响正常流程。
ShouldBindJSON
方法是最常用解析JSON数据的方法之一,但在重复调用的情况下会出现EOF的报错,这个原因出在ShouldBindJSON
在调用过一次之后context.request.body.sawEOF
的值是false导致,所以如果要多次绑定多个变量,需要使用ShouldBindBodyWith
。
Linux中访问
1 | curl localhost:8080/user/login -X POST -d '{"name":"xiaoming","pwd":123456}' -H "Content-Type: application/json" |
Windows自带的curl运行时,会改变发送的内容,比如这里想要发送一个json,它却自作聪明的去掉双引号,导致服务端不能正常解析。
需要修改为下面的形式:
1 | curl localhost:8080/user/login -X POST -d "{\"name\":\"xiaoming\",\"pwd\":\"123456\"}" -H "Content-Type: application/json" |
示例代码
1 | type UserModel struct { |
多次绑定
1 | // 以下是用于存储JSON数据的结构体 |
设置静态文件
1 | func main() { |
读取配置文件
1 | go get github.com/spf13/viper |
添加配置文件config.toml
1 | app_name = "z-ad" |
读取
1 | package main |
读取转字符串
方式1 这种方式只有string类型才能转换
1 | app_name := viper.Get("app_name").(string) |
方式2 这种方式什么都可以转换
1 | mysql_port := fmt.Sprint(viper.Get("mysql.port")) |
读取到结构体
1 | package main |
注意
转换的时候是不区分大小写的,但是不会自动去
_
的,如app_name
可以使用App_Name
取值,而AppName
是取不到值的。
保存配置
有时候,我们想要将程序中生成的配置,或者所做的修改保存下来。viper 提供了接口!
WriteConfig
:将当前的 viper 配置写到预定义路径,如果没有预定义路径,返回错误。将会覆盖当前配置;SafeWriteConfig
:与上面功能一样,但是如果配置文件存在,则不覆盖;WriteConfigAs
:保存配置到指定路径,如果文件存在,则覆盖;SafeWriteConfigAs
:与上面功能一样,但是如果配置文件存在,则不覆盖。
示例
1 | package main |
监听文件变化
viper 可以监听文件修改,热加载配置。因此不需要重启服务器,就能让配置生效。
1 | package main |
只需要调用viper.WatchConfig
,viper 会自动监听配置修改。如果有修改,重新加载的配置。
上面程序中,我们先打印redis.port
的值,然后Sleep
10s。在这期间修改配置中redis.port
的值,Sleep
结束后再次打印。 发现打印出修改后的值:
1 | redis port before sleep: 7381 |
另外,还可以为配置修改增加一个回调:
1 | viper.OnConfigChange(func(e fsnotify.Event) { |
这样文件修改时会执行这个回调。
MySQL
下载依赖
1 | go get github.com/go-sql-driver/mysql |
初始化
1 | package db |
查询
实体类
1 | type Tuser struct { |
查询
1 | // Login 查询单个用户 |
调用时
1 | import ( |
其中
- Get 查询单条数据
- Select 查询多条数据
注意
defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,
也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
连接池的实现关键在于SetMaxOpenConns和SetMaxIdleConns,其中:
SetMaxOpenConns用于设置最大打开的连接数,默认值为0表示不限制。
SetMaxIdleConns用于设置闲置的连接数。
设置最大的连接数,可以避免并发太高导致连接mysql出现too many connections的错误。
设置闲置的连接数则当开启的一个连接使用完成后可以放在池里等候下一次使用。
增删改
1 | // AddUser 添加 返回自增长字段的值 |
查询使不使用泛型对比
不使用泛型
1 | func GetRecord(dest interface{}, query string, args ...interface{}) bool { |
调用
1 | // GetUser 查询单个用户 |
使用泛型
1 | func GetRecord[T any](query string, args ...interface{}) *T { |
调用
1 | // GetUser 查询单个用户 |
这样代码就好多了
整体代码
db/db.go
1 | package db |
model/model_user.go
1 | package model |
db/dbuser.go
1 | package db |
调用
1 | import ( |