这是我参与「第五届青训营 」笔记创作活动的第2天。
在学习hertz时,发现对go原本的http框架并不是很熟悉,所以参考了视频学习了gin,有了对http框架更加形象的认识
1. 安装
- gin 的的依赖,使用 go get 获取
$ go get -u github.com/gin-gonic/gin
- 使用 go mod 来管理项目
go mod init gin-demo
- 在执行文件
main.go中引入 gin 以及 http 包
import (
"github.com/gin-gonic/gin"
"net/http" // 用于使用诸如 `http.StatusOK` 之类的常量
)
2. 启动
以一个get请求为例,启动一个web后端的步骤主要分为三步:
- 使用
gin.Default()初始化一个带有默认中间件的web引擎:server - 构造 get 请求
- 使用
server.run启动服务,并且开始监听路由,此时如果传入:xxxx,则服务的端口号将变为xxxx(默认是8080端口)
package main
import "github.com/gin-gonic/gin"
func main() {
server := gin.Default()
server.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
server.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
3. web开发中常用
3.1 Restful API
gin的引擎提供了四大类型的请求方式,可以使用类似于get请求的方式构造其他请求方式的请求。使用Restful API的方式,使用用一个一个路由即可以代表多种 method 的请求。
- server.GET("/user", ...)
- server.POST("/user", ...)
- server.PUT("/user", ...)
- server.DELETE("/user", ...)
3.2 响应页面
前后端之间没有用json数据传递时,请求中会响应页面,此时需要将html页面返回出去,并且加上需要附加的数据。
- 首先设置html文件的位置
server.LoadHTMLGlob("./templates/*")
- 随后在请求中使用 HTML() 方法,设置当前响应的页面名称以及相应的数据
server.GET("/index", func(context *gin.Context) {
context.HTML(http.StatusOK, "index.html", gin.H{"msg": "这是一个首页"})
})
- 在静态文件中使用模板的语法写入需要填充的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>{{.msg}}</p>
</body>
</html>
404页面的响应
404为找不到路由,所以只要调用 NoRoute 方法即可。
server.NoRoute(func(context *gin.Context) {
context.HTML(http.StatusNotFound, "404.html", nil)
})
3.3 获取url中传参
3.3.1 Query方式
当路由使用的是 url?param1=xxx¶m2=yyy 时,使用 context.Query 方法获取对应key的value。
server.POST("/user", func(context *gin.Context) {
username := context.Query("username")
passwd := context.Query("passwd")
context.JSON(http.StatusOK, gin.H{
"username": username,
"passwd": passwd,
})
})
3.3.2 Param方式
当传递的参数就在路由中时,可以使用 context.Param() 方法获取,此时需要在请求的路由中加上 : 或 *,两个字符的意义的区别如下:
- "user/:name":该参数需要严格匹配到name,如果只有"user(/)",并没有后序的字符串,则无法匹配该路由
- "user/:name/*action":*可以理解为任意字符串,包括空字符串,他可以匹配"user/awsling/abc"。当action处的字符串为空时,也可以匹配,匹配到的路由是"user/awsling/",此时action对应的参数是"/"。(action匹配的字符串会把"/"加上)
server.POST("/user/:username/*action", func(context *gin.Context) {
username := context.Param("username")
action := context.Param("action")
context.JSON(http.StatusOK, gin.H{
"username": username,
"action": action,
})
}
3.3.3 获取request中的body
当前端传递的是json数据,一般都是从body中传过来的,此时需要从context中获取body数据,此时从body中拿出的是byte数组,即序列化过的结果,需要将其反序列化到map中,在处理请求。
server.POST("/user/login", func(context *gin.Context) {
data, err := context.GetRawData()
if err != nil {
log.Println(err.Error())
return
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
log.Println(err.Error())
return
}
context.JSON(http.StatusOK, m)
})
3.3.4 获取表单中的数据
当前后端不分离时,提交的表单通过form的形式传递过来,此时需要使用 server.PostForm() 方法获取表单的数据
server.POST("/user/register", func(context *gin.Context) {
username := context.PostForm("username")
passwd := context.PostForm("passwd")
context.JSON(http.StatusOK, gin.H{
"username": username,
"passwd": passwd,
})
})
3.4 路由管理
由于路由到后面会越来越多,所以需要将路由进行分组,以对路由进行分类管理。 使用 server.Group("/xxx") 为 xxx 分配路由组,随后注册路由到路由组中。
userGroup := server.Group("/user")
{
userGroup.GET("/info")
userGroup.POST("/login")
userGroup.POST("/register")
}
orderGroup := server.Group("/order")
{
orderGroup.GET("/info")
orderGroup.POST("/info")
}
3.5 使用中间件
中间件可以看做是请求的拦截处理器,用于处理请求前,请求中,请求后的一系列操作。 gin.Default() 默认会带有 logger、recover 两个中间件;如果不想使用原始带有的中间件,则需要使用 gin.New() 创建服务。
配置中间件有三种方法:
- 全局中间件,所有的请求都会使用该中间件。
server.Use(handler()):
- 路由配置中间件,对于每个路由,可以配置任意数量的中间件
server.GET("/benchmark", MyBenchLogger(), ...)
- 路由组配置中间件,有两种方式
// 在此例中,我们在 "authorized" 路由组中使用自定义创建的
// AuthRequired() 中间件
// 一步完成
authorized := r.Group("/", AuthRequired())
// 上一句代码和使用以下两行代码的效果完全一样:
authorized := r.Group("/")
Authorized.Use(AuthRequired())