1.gin框架入门
1.1 介绍
Gin 是一个用 Golang编写的 高性能的web 框架, 由于http路由的优化,速度提高了近 40 倍。 Gin的特点就是封装优雅、API友好。
Gin的一些特性:
- 快速
基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。 - 支持中间件
传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。 - Crash 处理
Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic! - JSON 验证
Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。 - 路由组
更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。 - 错误管理
Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。 - 内置渲染
Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。 - 可扩展性
新建一个中间件非常简单。
1.2 安装
在go安装目录下 E:\golang\src
执行 cmd
:
-
开启 go module
set GO111MODULE=on //windows export GO111MODULE=on //linux
-
切换代理
go env -w GOPROXY=https://goproxy.cn,direct
-
开始下载Gin
go get -u github.com/gin-gonic/gin // go前面有一个空格
如果报错:
$GOPATH/go.mod exists but should not
,则删除go项目文件中的go.mod
文件
1.3 一个简单http server的例子
package main
import "github.com/gin-gonic/gin"
func main() {
// 初始化一个http服务对象
r := gin.Default()
// 设置一个GET请求的路由,url: '/ping', 控制器函数: 闭包
r.GET("/ping", func(c *gin.Context) {
// 通过请求上下文对象Context,返回json
c.JSON(200, gin.H{
"message": "pong",
})
})
// 监听,并在 localhost:8080上启动服务
r.Run()
}
我就想试试github.com/gin-gonic/gin,为什么死活都run不起来?
要运行一个项目步骤 (Win) :
安装golang
下载安装包安装
在cmd中输入go回车,有输出则说明安装正常
一般安装的时候程序会自动添加,无需人工干预
检查GOPATH
可以在cmd中查看set GOPATH
或者在"我的电脑"-“属性”-“高级”-"环境变量"中查看和添加
正常go安装,会自动添加,本机GOPATH=C:\Users\Administrator\go;
安装gin
接下来安装gin,框架文档介绍: go get -u github.com/gin-gonic/gin
这时候开始遇到问题了,常遇问题资源加载不了,解决方法是使用代理(这块有个 go env 的命令,可以查看当前配置),在cmd中运行:
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.io,direct
设置后,重新运行: go get -u github.com/gin-gonic/gin,可以很快速的安装
运行项目
在C:\Users\Administrator\go\ 下创建 src/gin/ 文件夹,创建main.go 文件,添加上面的代码
在cmd中,进入C:\Users\Administrator\go\src\gin\目录,运行命令: go run .\main.go
这时候问题又来了,报错:main.go:3:8: cannot find module providing package github.com/gin-gonic/gin: working directory is not part of a module
解决方法,当前目录分别运行下面两句代码:
go mod init gin go mod edit -require github.com/gin-gonic/gin@latest
上面运行后,再运行 go run .\main.go,就可行了。
1.4 项目结构
Gin框没有对项目结构做出限制,因此可以根据项目需要自行设计。
一个典型的MVC框架:
├── conf # 项目配置文件目录
│ └── config.toml # 可以选择熟悉的配置文件管理工具包例如:toml、xml等等
├── controllers # 控制器目录,按模块存放控制器(或者叫控制器函数),必要的时候可以继续划分子目录。
│ ├── food.go
│ └── user.go
├── main.go # 项目入口,这里负责Gin框架的初始化,注册路由信息,关联控制器函数等。
├── models # 模型目录,负责项目的数据存储部分,例如各个模块的Mysql表的读写模型。
│ ├── food.go
│ └── user.go
├── static # 静态资源目录,包括Js,css,jpg等等,可以通过Gin框架配置,直接让用户访问。
│ ├── css
│ ├── images
│ └── js
├── logs # 日志文件目录,主要保存项目运行过程中产生的日志。
└── views # 视图模板目录,存放各个模块的视图模板,当然有些项目只有api,是不需要视图部分,可以忽略这个目录
└── index.html
1.5 Gin框架运行模式
为了方便调试,Gin 框架在运行的时候默认是debug模式,在控制台默认会打印出很多调试日志。
上线的时候需要关闭debug模式,改为release模式。
设置Gin框架运行模式:
-
通过环境变量设置
export GIN_MODE=release / debug
-
通过代码设置
在main函数,初始化gin框架的时候执行下面代码 // 设置 release模式 gin.SetMode(gin.ReleaseMode) // 或者 设置debug模式 gin.SetMode(gin.DebugMode)
2.Gin路由与控制器
2.1 概述
路由是一个过程,指的是一个http请求,如何找到对应的处理器函数(控制器函数)。
Gin框架的路由是基于httprouter包实现的。
控制器函数主要负责执行http请求-响应任务。
r := gin.Default()
// 路由定义post请求, url路径为:/user/login, 绑定doLogin控制器函数
r.POST("/user/login", doLogin)
// 控制器函数
func doLogin(c *gin.Context) {
// 获取post请求参数
username := c.PostForm("username")
password := c.PostForm("password")
// 通过请求上下文对象Context, 直接往客户端返回一个字符串
c.String(200, "username=%s,password=%s", username,password)
}
2.2 路由规则
路由规则由三部分组成:
- http请求方法
- url路径
- 控制器函数
2.2.1 http请求方法
常用的http请求方法有下面4种:
- GET
- POST
- PUT
- DELETE
2.2.2 url路径
echo框架,url路径有三种写法:
- 静态url路径
- 带路径参数的url路径
- 带星号(*)模糊匹配参数的url路径
例子1, 静态Url路径, 即不带任何参数的url路径
/users/center
/user/111
/food/12
例子2,带路径参数的url路径,url路径上面带有参数,参数由冒号(:)跟着一个字符串定义。
路径参数值可以是数值,也可以是字符串
//定义参数:id, 可以匹配/user/1, /user/899 /user/xiaoli 这类Url路径
/user/:id
//定义参数:id, 可以匹配/food/2, /food/100 /food/apple 这类Url路径
/food/:id
//定义参数:type和:page, 可以匹配/foods/2/1, /food/100/25 /food/apple/30 这类Url路径
/foods/:type/:page
例子3. 带星号(*)模糊匹配参数的url路径
星号代表匹配任意路径的意思, 必须在号后面指定一个参数名,后面可以通过这个参数获取号匹配的内容。
// 以 /foods/ 开头的所有路径都匹配, 匹配:/foods/1, /foods/200, /foods/1/20, /foods/apple/1
/foods/*path
可以通过path参数获取*号匹配的内容。
2.2.3 控制器函数
控制器函数定义:
func HandlerFunc(c *gin.Context)
控制器函数接受一个上下文参数。
可以通过上下文参数,获取http请求参数,响应http请求。
2.2.4 路由定义例子
//实例化gin实例对象。
r := gin.Default()
//定义post请求, url路径为:/users, 绑定saveUser控制器函数
r.POST("/users", saveUser)
//定义get请求,url路径为:/users/:id (:id是参数,例如: /users/10, 会匹配这个url模式),绑定getUser控制器函数
r.GET("/users/:id", getUser)
//定义put请求
r.PUT("/users/:id", updateUser)
//定义delete请求
r.DELETE("/users/:id", deleteUser)
//控制器函数实现
func saveUser(c *gin.Context) {
...忽略实现...
}
func getUser(c *gin.Context) {
...忽略实现...
}
func updateUser(c *gin.Context) {
...忽略实现...
}
func deleteUser(c *gin.Context) {
...忽略实现...
}
提示:实际项目开发中不要把路由定义和控制器函数都写在一个go文件,不方便维护,可以参考第一章的项目结构,规划自己的业务模块。
2.3 分组路由
在做api开发的时候,如果要支持多个api版本,可以通过分组路由来实现api版本处理。
func main() {
router := gin.Default()
// 创建v1组
v1 := router.Group("/v1")
{
// 在v1这个分组下,注册路由
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// 创建v2组
v2 := router.Group("/v2")
{
// 在v2这个分组下,注册路由
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080"