Gin框架的路由
1.1 默认服务器
router.Run()
除了默认服务器中router.Run()的方式外,还可以用http.ListenAndServe(),比如
func main() {
router := gin.Default()
http.ListenAndServe(":8080", router)
}
运行:
浏览器效果:
或者自定义HTTP服务器的配置:
func main() {
router := gin.Default()
s := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
2.1 基本路由
基本路由 gin 框架中采用的路由库是 httprouter。
// 创建带有默认中间件的路由:
// 日志与恢复中间件
router := gin.Default()
//创建不带中间件的路由:
//r := gin.New()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
gin的路由来自httprouter库。因此httprouter具有的功能,gin也具有,不过gin不支持路由正则表达式。
api 参数通过Context的Param方法来获取。
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, name)
})
运行后浏览器输入:http://127.0.0.1:8000/user/hanru
冒号:加上一个参数名组成路由参数。可以使用c.Params的方法读取其值。当然这个值是字串string。诸如/user/hanru,和/user/hello都可以匹配,而/user/和/user/hanru/不会被匹配。
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
浏览器中输入:http://127.0.0.1:8000/user/hanru/send
除了:,gin还提供了号处理参数,号能匹配的规则就更多。
web提供的服务通常是client和server的交互。其中客户端向服务器发送请求,除了路由参数,其他的参数无非两种,查询字符串query string和报文体body参数。所谓query string,即路由用,用?以后连接的key1=value2&key2=value2的形式的参数。当然这个key-value是经过urlencode编码。
URL 参数通过 DefaultQuery 或 Query 方法获取。
对于参数的处理,经常会出现参数不存在的情况,对于是否提供默认值,gin也考虑了,并且给出了一个优雅的方案,使用c.DefaultQuery方法读取参数,其中当参数不存在的时候,提供一个默认值。使用Query方法读取正常参数,当参数不存在的时候,返回空字串。
func main() {
router := gin.Default()
router.GET("/welcome", func(c *gin.Context) {
name := c.DefaultQuery("name", "Guest") //可设置默认值
//nickname := c.Query("nickname") // 是 c.Request.URL.Query().Get("nickname") 的简写
c.String(http.StatusOK, fmt.Sprintf("Hello %s ", name))
})
router.Run(":9527")
}
当浏览器输入的url为:http://127.0.0.1:9527/welcom?name=hanru
我们可以看到能够显示我们的参数数据,如果没有传递参数,那么就会显示默认值,url为:http://127.0.0.1:9527/welcom
http的报文体传输数据就比query string稍微复杂一点,常见的格式就有四种。例如application/json,application/x-www-form-urlencoded,application/xml和multipart/form-data。后面一个主要用于图片上传。json格式的很好理解,urlencode其实也不难,无非就是把query string的内容,放到了body体里,同样也需要urlencode。默认情况下,c.PostFROM解析的是x-www-form-urlencoded或from-data的参数。
表单参数通过 PostForm 方法获取:
func main() {
router := gin.Default()
//form
router.POST("/form", func(c *gin.Context) {
type1 := c.DefaultPostForm("type", "alert") //可设置默认值
username := c.PostForm("username")
password := c.PostForm("password")
//hobbys := c.PostFormMap("hobby")
//hobbys := c.QueryArray("hobby")
hobbys := c.PostFormArray("hobby")
c.String(http.StatusOK, fmt.Sprintf("type is %s, username is %s, password is %s,hobby is %v", type1, username, password,hobbys))
})
router.Run(":9527")
}
我们还需要提供一个html页面(login.html),来进行post请求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="http://127.0.0.1:9527/form" method="post" enctype="application/x-www-form-urlencoded">
用户名:<input type="text" name="username">
<br>
密   码:<input type="password" name="password">
<br>
兴   趣:
<input type="checkbox" value="girl" name="hobby">女人
<input type="checkbox" value="game" name="hobby">游戏
<input type="checkbox" value="money" name="hobby">金钱
<br>
<input type="submit" value="登录">
</form>
</body>
</html>
然后运行程序后,通过浏览器访问页面:
输入用户名和密码后,点击按钮进行登录:
username和password数据我们可以获取,type获取不到就使用默认值。
使用PostForm形式,注意必须要设置Post的type,同时此方法中忽略URL中带的参数,所有的参数需要从Body中获得。
上传单个文件
前面介绍了基本的发送数据,其中multipart/form-data转用于文件上传。gin文件上传也很方便,和原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中了。
首先我们创建一个go文件,demo06_file.go:
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)
// Upload the file to specific dst.
c.SaveUploadedFile(file, file.Filename)
/*
也可以直接使用io操作,拷贝文件数据。
out, err := os.Create(filename)
defer out.Close()
_, err = io.Copy(out, file)
*/
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
使用c.Request.FormFile解析客户端文件name属性。如果不传文件,则会抛错,因此需要处理这个错误。此处我们略写了错误处理。一种是直接用c.SaveUploadedFile()保存文件。另一种方式是使用os的操作,把文件数据复制到硬盘上。
然后我们创建一个html页面,file.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
头像:
<input type="file" name="file">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
运行程序后,打开浏览器传递文件:
点击按钮后进行提交上传:
显示已经上传,我们可以在项目目录下查看文件:
我们可以看到已经上传成功了一张图片。
我们也可以使用终端命令访问http,上传文件,我们打算传这个视频:
打开终端,并输入以下命令:
hanru:~ ruby$ curl -X POST http://127.0.0.1:8080/upload -F "file=@/Users/ruby/Documents/pro/momo.mp4" -H "Content-Type: multipart/form-data"
我们可以看到这个视频文件已经上传到了项目的目录下。
所谓多个文件,无非就是多一次遍历文件,然后一次copy数据存储即可。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"fmt"
)
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
//router.Static("/", "./public")
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
return
}
files := form.File["files"]
for _, file := range files {
if err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
}
c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files ", len(files)))
})
router.Run(":8080")
}
然后我们提供一个html页面,当然也可以使用终端命令:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件s</title>
</head>
<body>
<h1>上传多个文件</h1>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
Files: <input type="file" name="files" multiple><br><br>
<input type="submit" value="提交">
</form>
</body>
</html>
然后启动程序后,打开浏览器:
然后进行上传:
最后打开一下项目目录,查看刚刚上传的文件:
使用终端命令也可以:
curl -X POST http://localhost:8080/upload \
-F "upload[]=@/Users/ruby/Documents/pro/aa.jpeg" \
-F "upload[]=@/Users/ruby/Documents/pro/ad.txt" \
-H "Content-Type: multipart/form-data"
与单个文件上传类似,只不过使用了c.Request.MultipartForm得到文件句柄,再获取文件数据,然后遍历读写。
router group是为了方便一部分相同的URL的管理,新建一个go文件(demo08_group.go),
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"fmt"
)
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.GET("/login", loginEndpoint)
v1.GET("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
func loginEndpoint(c *gin.Context) {
name := c.DefaultQuery("name", "Guest") //可设置默认值
c.String(http.StatusOK, fmt.Sprintf("Hello %s \n", name))
}
func submitEndpoint(c *gin.Context) {
name := c.DefaultQuery("name", "Guest") //可设置默认值
c.String(http.StatusOK, fmt.Sprintf("Hello %s \n", name))
}
func readEndpoint(c *gin.Context) {
name := c.DefaultQuery("name", "Guest") //可设置默认值
c.String(http.StatusOK, fmt.Sprintf("Hello %s \n", name))
}
运行程序后,可以通过一个html页面访问,也可以通过终端使用命令直接访问,此处我们使用终端:
hanru:~ ruby$ curl http://127.0.0.1:8080/v1/login?name=hanru
运行结果如下: