ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### 【GoWeb开发实战】Gin框架\_Router # Gin框架的路由 ## 一、服务器 ## 1.1 默认服务器 ~~~go router.Run() ~~~ ## 1.2 Http服务器 除了默认服务器中`router.Run()`的方式外,还可以用`http.ListenAndServe()`,比如 ~~~go func main() { router := gin.Default() http.ListenAndServe(":8080", router) } ~~~ 运行: ![gin_yunxing3](http://image.chaindesk.cn/gin_yunxing3.png/mark) 浏览器效果: ![gin_yunxing4](http://image.chaindesk.cn/gin_yunxing4.png/mark) 或者自定义HTTP服务器的配置: ~~~go 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。 ~~~go // 创建带有默认中间件的路由: // 日志与恢复中间件 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) ~~~ ### 2.2 路由参数 gin的路由来自httprouter库。因此httprouter具有的功能,gin也具有,不过gin不支持路由正则表达式。 #### 2.2.1 API参数 api 参数通过Context的Param方法来获取。 ~~~go 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](http://127.0.0.1:8000/user/hanru) ![gin_yunxing9](http://image.chaindesk.cn/gin_yunxing9.png/mark) 冒号`:`加上一个参数名组成路由参数。可以使用c.Params的方法读取其值。当然这个值是字串string。诸如`/user/hanru`,和`/user/hello`都可以匹配,而`/user/`和`/user/hanru/`不会被匹配。 ~~~go 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](http://127.0.0.1:8000/user/hanru/send) ![gin_yunxing10](http://image.chaindesk.cn/gin_yunxing10.png/mark) 除了`:`,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方法读取正常参数,当参数不存在的时候,返回空字串。 ~~~go 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](http://127.0.0.1:9527/welcom?name=hanru) ![gin_yunxing5](http://image.chaindesk.cn/gin_yunxing5.png/mark) 我们可以看到能够显示我们的参数数据,如果没有传递参数,那么就会显示默认值,url为:[http://127.0.0.1:9527/welcom](http://127.0.0.1:9527/welcom) ![gin_yunxing6](http://image.chaindesk.cn/gin_yunxing6.png/mark) #### 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 方法获取: ~~~go 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请求: ~~~html <!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> 密&nbsp&nbsp&nbsp码:<input type="password" name="password"> <br> 兴&nbsp&nbsp&nbsp趣: <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> ~~~ 然后运行程序后,通过浏览器访问页面: ![gin_yunxing7](http://image.chaindesk.cn/gin_yunxing7.png/mark) 输入用户名和密码后,点击按钮进行登录: ![gin_yunxing8](http://image.chaindesk.cn/gin_yunxing8.png/mark) 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: ~~~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: ~~~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> ~~~ 运行程序后,打开浏览器传递文件: ![gin_yunxing11](http://image.chaindesk.cn/gin_yunxing11.png/mark) 点击按钮后进行提交上传: ![gin_yunxing12](http://image.chaindesk.cn/gin_yunxing12.png/mark) 显示已经上传,我们可以在项目目录下查看文件: ![gin_yunxing13](http://image.chaindesk.cn/gin_yunxing13.png/mark) 我们可以看到已经上传成功了一张图片。 我们也可以使用终端命令访问http,上传文件,我们打算传这个视频: ![gin_file1](http://image.chaindesk.cn/gin_file1.png/mark) 打开终端,并输入以下命令: ~~~shell 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" ~~~ ![gin_yunxing14](http://image.chaindesk.cn/gin_yunxing14.png/mark) 我们可以看到这个视频文件已经上传到了项目的目录下。 ##### 上传多个文件 所谓多个文件,无非就是多一次遍历文件,然后一次copy数据存储即可。 ~~~go 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页面,当然也可以使用终端命令: ~~~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> ~~~ 然后启动程序后,打开浏览器: ![gin_yunxing15](http://image.chaindesk.cn/gin_yunxing15.png/mark) 然后进行上传: ![gin_yunxing16](http://image.chaindesk.cn/gin_yunxing16.png/mark) 最后打开一下项目目录,查看刚刚上传的文件: ![gin_yunxing17](http://image.chaindesk.cn/gin_yunxing17.png/mark) 使用终端命令也可以: ~~~shell 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`得到文件句柄,再获取文件数据,然后遍历读写。 #### 2.2.5 Grouping routes router group是为了方便一部分相同的URL的管理,新建一个go文件(demo08\_group.go), ~~~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页面访问,也可以通过终端使用命令直接访问,此处我们使用终端: ~~~shell hanru:~ ruby$ curl http://127.0.0.1:8080/v1/login?name=hanru ~~~ 运行结果如下: ![gin_yunxing18](http://image.chaindesk.cn/gin_yunxing18.png/mark) [源代码](https://github.com/rubyhan1314/ginweb)