Router
基本路由
- 基本路由 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参数
- api 参数通过Context的Param方法来获取。(除了
:
,gin还提供了*
号处理参数,*
号能匹配的规则就更多。)
URL参数
- web提供的服务通常是client和server的交互。其中客户端向服务器发送请求,除了路由参数,其他的参数无非两种,查询字符串query string和报文体body参数。所谓query string,即路由用,用?以后连接的key1=value2&key2=value2的形式的参数。当然这个key-value是经过urlencode编码。
- URL 参数通过
DefaultQuery
或Query
方法获取。 - 对于参数的处理,经常会出现参数不存在的情况,对于是否提供默认值,gin也考虑了,并且给出了一个优雅的方案,使用
c.DefaultQuery
方法读取参数,其中当参数不存在的时候,提供一个默认值。使用Query
方法读取正常参数,当参数不存在的时候,返回空字串。
表单参数
- 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
方法获取:router.POST("/form", func(context *gin.Context) { //type1 := context.DefaultPostForm("type1", "male") // 默认值方法 uname := context.PostForm("uname") upwd := context.PostForm("upwd") //hobbys := context.PostFormMap("hobby") //hobbys := context.QueryArray("hobby") sex := context.PostFormArray("sex") context.String(http.StatusOK, fmt.Sprintf("uname is %s, upwd is %s, sex is %v", uname, upwd, sex)) })
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="http://localhost/form" method="post" enctype="application/x-www-form-urlencoded"> <table> <tr> <td>用户名</td> <td><input type="text" name="uname"></td> </tr> <tr> <td>密码</td> <td><input type="password" name="upwd"></td> </tr> <tr> <td>性别</td> <td> <input type="checkbox" value="男人" name="sex">男人 <input type="checkbox" value="女人" name="sex">女人 </td> </tr> <tr> <td></td> <td><input type="submit" value="提交" /></td> </tr> </table> </form> </body> </html>
文件上传
上传单个文件
- 前面介绍了基本的发送数据,其中multipart/form-data转用于文件上传。gin文件上传也很方便,和原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中了。
// 文件上传 router.POST("/upload", func(context *gin.Context) { // 单文件上传 file, _ := context.FormFile("file") log.Println(file.Filename) // 将文件上载 context.SaveUploadedFile(file, file.Filename) /* 也可以直接使用io操作,拷贝文件数据。 out, err := os.Create(filename) defer out.Close() _, err = io.Copy(out, file) */ context.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) })
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="http://localhost/upload" method="post" enctype="multipart/form-data"> <table> <tr> <td>文件</td> <td><input type="file" name="file"></td> </tr> <tr> <td></td> <td><input type="submit" value="提交" /></td> </tr> </table> </form> </body> </html>
上传多个文件
- 所谓多个文件,无非就是多一次遍历文件,然后一次copy数据存储即可。
router.POST("/uploads", func(context *gin.Context) { // 多文件上传 form, err := context.MultipartForm() if err != nil { context.String(http.StatusBadRequest, fmt.Sprintf("upload err is %:", err.Error())) return } files := form.File["files"] for _, file := range files { if err := context.SaveUploadedFile(file, file.Filename); err != nil { context.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error())) return } } context.String(http.StatusOK, fmt.Sprintf("upload succ %d files", len(files))) })
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="http://localhost/uploads" method="post" enctype="multipart/form-data"> <table> <tr> <td>多文件</td> <td><input type="file" name="files" multiple></td> </tr> <tr> <td></td> <td><input type="submit" value="提交" /></td> </tr> </table> </form> </body> </html>
- 与单个文件上传类似,只不过使用了
c.Request.MultipartForm
得到文件句柄,再获取文件数据,然后遍历读写。
- 与单个文件上传类似,只不过使用了
Grouping routes
- router group是为了方便一部分相同的URL的管理。
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func main() { router := gin.Default() // router group v1 v1 := router.Group("/v1") { v1.GET("/login", loginEndpoint) v1.GET("/submit", submitEndpoint) v1.POST("/read", readEndpoint) } // router group v2 v2 := router.Group("/v2") { v2.POST("/login", loginEndpoint) v2.POST("/submit", submitEndpoint) v2.POST("/read", readEndpoint) } router.Run(":80") } func loginEndpoint(context *gin.Context) { name := context.DefaultQuery("name", "Guest") // 默认值 context.String(http.StatusOK, fmt.Sprintf("登录 %s", name)) } func submitEndpoint(context *gin.Context) { name := context.DefaultQuery("name", "Guest") // 默认值 context.String(http.StatusOK, fmt.Sprintf("提交 %s", name)) } func readEndpoint(context *gin.Context) { name := context.DefaultQuery("name", "Guest") // 默认值 context.String(http.StatusOK, fmt.Sprintf("读取 %s", name)) }