参照:
https://www.chaindesk.cn/witbook/19/329
https://www.liwenzhou.com/posts/Go/Gin_framework/
1. net/http
1.1 使用文件返回
2. gin框架
package main import ( "github.com/gin-gonic/gin" "net/http" ) /* 1、router:=gin.Default():这是默认的服务器。使用gin的Default方法创建一个路由Handler; 2、然后通过Http方法绑定路由规则和路由函数。不同于net/http库的路由函数,gin进行了封装,把request和response都封装到了gin.Context的上下文环境中。 3、最后启动路由的Run方法监听端口。还可以用http.ListenAndServe(":8080", router),或者自定义Http服务器配置。 */ func main() { // 创建一个默认的路由引擎 r := gin.Default() //带有默认中间件的路由 //engine := New() //不带中间件的路由 // GET:请求方式;/hello:请求的路径 // 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数 r.GET("/hello", func(c *gin.Context) { // c.JSON:返回JSON格式的数据 //gin.H --> type H map[string]interface{} c.JSON(200, gin.H{ "message": "Hello world!", }) }) // 启动HTTP服务,默认在0.0.0.0:8080启动服务 r.Run() //也可以使用下面的方式启动服务 //http.ListenAndServe(":8080", router) } //default源码 func Default() *Engine { debugPrintWARNINGDefault() engine := New() //不带中间件的路由 engine.Use(Logger(), Recovery()) return engine } //Run源码 func (engine *Engine) Run(addr ...string) (err error) { defer func() { debugPrintError(err) }() address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n", address) err = http.ListenAndServe(address, engine) //engine就是r := gin.Default() return }
3. RESTful API
对调试不友好,可以使用postman调试
4. 路由
4.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)
4.1 API参数
gin的路由来自httprouter库。因此httprouter具有的功能,gin也具有,不过gin不支持路由正则表达式。
冒号:
加上一个参数名组成路由参数。可以使用c.Params的方法读取其值。当然这个值是字串string。
除了:
,gin还提供了*
号处理参数,*
号能匹配的规则就更多。
package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.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) }) r.Run() }
4.2 URL参数
web提供的服务通常是client和server的交互。其中客户端向服务器发送请求,除了路由参数,其他的参数无非两种,查询字符串query string和报文体body参数。所谓query string,即路由用,用?
以后连接的key1=value2&key2=value2
的形式的参数。当然这个key-value是经过urlencode编码。
URL 参数通过 DefaultQuery 或 Query 方法获取。
对于参数的处理,经常会出现参数不存在的情况,对于是否提供默认值,gin也考虑了,并且给出了一个优雅的方案,使用c.DefaultQuery方法读取参数,其中当参数不存在的时候,提供一个默认值。使用Query方法读取正常参数,当参数不存在的时候,返回空字串。
package main import ( "github.com/gin-gonic/gin" "net/http" "fmt" ) 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") }
4.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 方法获取:
package main import ( "github.com/gin-gonic/gin" "net/http" "fmt" )
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") }
使用PostForm形式,注意必须要设置Post的type,同时此方法中忽略URL中带的参数,所有的参数需要从Body中获得。
4.4 文件上传
上传单个文件
前面介绍了基本的发送数据,其中multipart/form-data
转用于文件上传。gin文件上传也很方便,和原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中了。
package main import ( "github.com/gin-gonic/gin" "net/http" "fmt" "log" ) 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. // func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {} 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") }
上传多个文件
所谓多个文件,无非就是多一次遍历文件,然后一次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") }
与单个文件上传类似,只不过使用了c.Request.MultipartForm
得到文件句柄,再获取文件数据,然后遍历读写。
5. 路由组
我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性一对{}
包裹同组的路由,这只是为了看着清晰,你用不用{}
包裹功能上没什么区别。
func main() { r := gin.Default() userGroup := r.Group("/user") { userGroup.GET("/index", func(c *gin.Context) {...}) userGroup.GET("/login", func(c *gin.Context) {...}) userGroup.POST("/login", func(c *gin.Context) {...}) } shopGroup := r.Group("/shop") { shopGroup.GET("/index", func(c *gin.Context) {...}) shopGroup.GET("/cart", func(c *gin.Context) {...}) shopGroup.POST("/checkout", func(c *gin.Context) {...}) } r.Run() }
路由组也是支持嵌套的,例如:
shopGroup := r.Group("/shop") { shopGroup.GET("/index", func(c *gin.Context) {...}) shopGroup.GET("/cart", func(c *gin.Context) {...}) shopGroup.POST("/checkout", func(c *gin.Context) {...}) // 嵌套路由组 xx := shopGroup.Group("xx") xx.GET("/oo", func(c *gin.Context) {...}) }