当前位置:  首页>> 技术小册>> Gin框架入门教程

Gin框架的路由

一、服务器

1.1 默认服务器
router.Run()

1.2 Http服务器

除了默认服务器中router.Run()的方式外,还可以用http.ListenAndServe(),比如

  1. func main() {
  2. router := gin.Default()
  3. http.ListenAndServe(":8080", router)
  4. }

运行:

浏览器效果:

或者自定义HTTP服务器的配置:

  1. func main() {
  2. router := gin.Default()
  3. s := &http.Server{
  4. Addr: ":8080",
  5. Handler: router,
  6. ReadTimeout: 10 * time.Second,
  7. WriteTimeout: 10 * time.Second,
  8. MaxHeaderBytes: 1 << 20,
  9. }
  10. s.ListenAndServe()
  11. }

二、路由

2.1 基本路由
基本路由 gin 框架中采用的路由库是 httprouter。

  1. // 创建带有默认中间件的路由:
  2. // 日志与恢复中间件
  3. router := gin.Default()
  4. //创建不带中间件的路由:
  5. //r := gin.New()
  6. router.GET("/someGet", getting)
  7. router.POST("/somePost", posting)
  8. router.PUT("/somePut", putting)
  9. router.DELETE("/someDelete", deleting)
  10. router.PATCH("/somePatch", patching)
  11. router.HEAD("/someHead", head)
  12. router.OPTIONS("/someOptions", options)

2.2 路由参数

gin的路由来自httprouter库。因此httprouter具有的功能,gin也具有,不过gin不支持路由正则表达式。

2.2.1 API参数

api 参数通过Context的Param方法来获取。

  1. router.GET("/user/:name", func(c *gin.Context) {
  2. name := c.Param("name")
  3. c.String(http.StatusOK, name)
  4. })

运行后浏览器输入:http://127.0.0.1:8000/user/hanru

冒号:加上一个参数名组成路由参数。可以使用c.Params的方法读取其值。当然这个值是字串string。诸如/user/hanru,和/user/hello都可以匹配,而/user/和/user/hanru/不会被匹配。

  1. router.GET("/user/:name/*action", func(c *gin.Context) {
  2. name := c.Param("name")
  3. action := c.Param("action")
  4. message := name + " is " + action
  5. c.String(http.StatusOK, message)
  6. })

浏览器中输入:http://127.0.0.1:8000/user/hanru/send

除了:,gin还提供了号处理参数,号能匹配的规则就更多。

2.2.2 URL参数

web提供的服务通常是client和server的交互。其中客户端向服务器发送请求,除了路由参数,其他的参数无非两种,查询字符串query string和报文体body参数。所谓query string,即路由用,用?以后连接的key1=value2&key2=value2的形式的参数。当然这个key-value是经过urlencode编码。

URL 参数通过 DefaultQuery 或 Query 方法获取。

对于参数的处理,经常会出现参数不存在的情况,对于是否提供默认值,gin也考虑了,并且给出了一个优雅的方案,使用c.DefaultQuery方法读取参数,其中当参数不存在的时候,提供一个默认值。使用Query方法读取正常参数,当参数不存在的时候,返回空字串。

  1. func main() {
  2. router := gin.Default()
  3. router.GET("/welcome", func(c *gin.Context) {
  4. name := c.DefaultQuery("name", "Guest") //可设置默认值
  5. //nickname := c.Query("nickname") // 是 c.Request.URL.Query().Get("nickname") 的简写
  6. c.String(http.StatusOK, fmt.Sprintf("Hello %s ", name))
  7. })
  8. router.Run(":9527")
  9. }

当浏览器输入的url为:http://127.0.0.1:9527/welcom?name=hanru

我们可以看到能够显示我们的参数数据,如果没有传递参数,那么就会显示默认值,url为:http://127.0.0.1:9527/welcom

2.2.3 表单参数

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 方法获取:

  1. func main() {
  2. router := gin.Default()
  3. //form
  4. router.POST("/form", func(c *gin.Context) {
  5. type1 := c.DefaultPostForm("type", "alert") //可设置默认值
  6. username := c.PostForm("username")
  7. password := c.PostForm("password")
  8. //hobbys := c.PostFormMap("hobby")
  9. //hobbys := c.QueryArray("hobby")
  10. hobbys := c.PostFormArray("hobby")
  11. c.String(http.StatusOK, fmt.Sprintf("type is %s, username is %s, password is %s,hobby is %v", type1, username, password,hobbys))
  12. })
  13. router.Run(":9527")
  14. }

我们还需要提供一个html页面(login.html),来进行post请求:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录</title>
  6. </head>
  7. <body>
  8. <form action="http://127.0.0.1:9527/form" method="post" enctype="application/x-www-form-urlencoded">
  9. 用户名:<input type="text" name="username">
  10. <br>
  11. 密&nbsp&nbsp&nbsp码:<input type="password" name="password">
  12. <br>
  13. 兴&nbsp&nbsp&nbsp趣:
  14. <input type="checkbox" value="girl" name="hobby">女人
  15. <input type="checkbox" value="game" name="hobby">游戏
  16. <input type="checkbox" value="money" name="hobby">金钱
  17. <br>
  18. <input type="submit" value="登录">
  19. </form>
  20. </body>
  21. </html>

然后运行程序后,通过浏览器访问页面:

输入用户名和密码后,点击按钮进行登录:

username和password数据我们可以获取,type获取不到就使用默认值。

使用PostForm形式,注意必须要设置Post的type,同时此方法中忽略URL中带的参数,所有的参数需要从Body中获得。

2.2.4 文件上传

上传单个文件
前面介绍了基本的发送数据,其中multipart/form-data转用于文件上传。gin文件上传也很方便,和原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中了。

首先我们创建一个go文件,demo06_file.go:

  1. func main() {
  2. router := gin.Default()
  3. // Set a lower memory limit for multipart forms (default is 32 MiB)
  4. // router.MaxMultipartMemory = 8 << 20 // 8 MiB
  5. router.POST("/upload", func(c *gin.Context) {
  6. // single file
  7. file, _ := c.FormFile("file")
  8. log.Println(file.Filename)
  9. // Upload the file to specific dst.
  10. c.SaveUploadedFile(file, file.Filename)
  11. /*
  12. 也可以直接使用io操作,拷贝文件数据。
  13. out, err := os.Create(filename)
  14. defer out.Close()
  15. _, err = io.Copy(out, file)
  16. */
  17. c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
  18. })
  19. router.Run(":8080")
  20. }

使用c.Request.FormFile解析客户端文件name属性。如果不传文件,则会抛错,因此需要处理这个错误。此处我们略写了错误处理。一种是直接用c.SaveUploadedFile()保存文件。另一种方式是使用os的操作,把文件数据复制到硬盘上。

然后我们创建一个html页面,file.html:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>文件</title>
  6. </head>
  7. <body>
  8. <form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
  9. 头像:
  10. <input type="file" name="file">
  11. <br>
  12. <input type="submit" value="提交">
  13. </form>
  14. </body>
  15. </html>

运行程序后,打开浏览器传递文件:

点击按钮后进行提交上传:

显示已经上传,我们可以在项目目录下查看文件:

我们可以看到已经上传成功了一张图片。

我们也可以使用终端命令访问http,上传文件,我们打算传这个视频:

打开终端,并输入以下命令:

  1. 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数据存储即可。

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "net/http"
  5. "fmt"
  6. )
  7. func main() {
  8. router := gin.Default()
  9. // Set a lower memory limit for multipart forms (default is 32 MiB)
  10. router.MaxMultipartMemory = 8 << 20 // 8 MiB
  11. //router.Static("/", "./public")
  12. router.POST("/upload", func(c *gin.Context) {
  13. // Multipart form
  14. form, err := c.MultipartForm()
  15. if err != nil {
  16. c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
  17. return
  18. }
  19. files := form.File["files"]
  20. for _, file := range files {
  21. if err := c.SaveUploadedFile(file, file.Filename); err != nil {
  22. c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
  23. return
  24. }
  25. }
  26. c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files ", len(files)))
  27. })
  28. router.Run(":8080")
  29. }

然后我们提供一个html页面,当然也可以使用终端命令:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>文件s</title>
  6. </head>
  7. <body>
  8. <h1>上传多个文件</h1>
  9. <form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
  10. Files: <input type="file" name="files" multiple><br><br>
  11. <input type="submit" value="提交">
  12. </form>
  13. </body>
  14. </html>

然后启动程序后,打开浏览器:

然后进行上传:

最后打开一下项目目录,查看刚刚上传的文件:

使用终端命令也可以:

  1. curl -X POST http://localhost:8080/upload \
  2. -F "upload[]=@/Users/ruby/Documents/pro/aa.jpeg" \
  3. -F "upload[]=@/Users/ruby/Documents/pro/ad.txt" \
  4. -H "Content-Type: multipart/form-data"

与单个文件上传类似,只不过使用了c.Request.MultipartForm得到文件句柄,再获取文件数据,然后遍历读写。

2.2.5 Grouping routes

router group是为了方便一部分相同的URL的管理,新建一个go文件(demo08_group.go),

  1. package main
  2. import (
  3. "github.com/gin-gonic/gin"
  4. "net/http"
  5. "fmt"
  6. )
  7. func main() {
  8. router := gin.Default()
  9. // Simple group: v1
  10. v1 := router.Group("/v1")
  11. {
  12. v1.GET("/login", loginEndpoint)
  13. v1.GET("/submit", submitEndpoint)
  14. v1.POST("/read", readEndpoint)
  15. }
  16. // Simple group: v2
  17. v2 := router.Group("/v2")
  18. {
  19. v2.POST("/login", loginEndpoint)
  20. v2.POST("/submit", submitEndpoint)
  21. v2.POST("/read", readEndpoint)
  22. }
  23. router.Run(":8080")
  24. }
  25. func loginEndpoint(c *gin.Context) {
  26. name := c.DefaultQuery("name", "Guest") //可设置默认值
  27. c.String(http.StatusOK, fmt.Sprintf("Hello %s \n", name))
  28. }
  29. func submitEndpoint(c *gin.Context) {
  30. name := c.DefaultQuery("name", "Guest") //可设置默认值
  31. c.String(http.StatusOK, fmt.Sprintf("Hello %s \n", name))
  32. }
  33. func readEndpoint(c *gin.Context) {
  34. name := c.DefaultQuery("name", "Guest") //可设置默认值
  35. c.String(http.StatusOK, fmt.Sprintf("Hello %s \n", name))
  36. }

运行程序后,可以通过一个html页面访问,也可以通过终端使用命令直接访问,此处我们使用终端:

hanru:~ ruby$ curl http://127.0.0.1:8080/v1/login?name=hanru
运行结果如下:


该分类下的相关小册推荐: