前言
gin框架是go語言的一個框架,框架的github地址是:https://github.com/gin-gonic/gin
轉載本文,請標注原文地址:https://www.cnblogs.com/-beyond/p/9391892.html
安裝gin框架
go get -u github.com/gin-gonic/gin
第一次使用gin框架
創建一個文件main.go,拷貝如下內容
package main import ( "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.GET("/get", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use get method"}) }) router.Run() }
使用 go run main.go 運行程序,程序默認綁定的是8080端口,測試使用瀏覽器訪問localhost:8080/get,會有如下response:
切換綁定端口
gin框架默認的是綁定到8080端口,但是可以切換端口,方法就是指定router.Run()的參數。
router.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) return }
可以看到,Run方法也只是使用了http的ListenAndServe方法而已
現在可以將端口綁定到9000端口,可以這樣寫:
router.Run(":9000")
gin.H{ }
第1個代碼例子中,有這么一行c.JSON(200, gin.H{"message": "use get method"})
這其中有一個gin.H{ },看樣子,這像是一個結構體struct,查看gin框架的源碼,聲明如下:
// H is a shortcut for map[string]interface{} type H map[string]interface{}
所以,這只是一個map結構,別以為是一個struct哈
設置http請求方式
gin框架封裝了http庫,提供了GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS這些http請求方式。
使用router.method()來綁定路由
聲明如下:
func (group *RouterGroup) METHOD(relativePath string, handlers ...HandlerFunc) IRoutes
其中的METHOD可以是上面的7種方式。
例子:
package main import ( "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.GET("/get", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use get method"}) }) router.POST("/post", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use post method"}) }) router.PUT("/put", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use put method"}) }) router.DELETE("/delete", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use delete method"}) }) router.PATCH("/patch", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use patch method"}) }) router.HEAD("/head", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use head method"}) }) router.OPTIONS("/options", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use options method"}) }) router.Run() }
使用對應的method訪問對應的path,會的對應的response。
但是如果使用不對應的method訪問path,就會返回404,比如使用post方法訪問localhost:8080/get會返回404。
切換輸出的格式
看下面的代碼:
router.GET("/get", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use get method"}) })
上面的這段代碼,就是在用戶訪問localhost:8080時,c.JSON()返回一個響應,響應中的狀態碼是200,響應內容是一個JSON格式的字符串。
那么我們就很容易知道,不同的響應內容格式是通過調用*gin.Context類型變量的不同方法來實現的。
gin框架提供了很多響應內容的格式,常用的方法聲明如下:
//返回json格式 func (c *Context) JSON(code int, obj interface{}) //返回xml格式 func (c *Context) XML(code int, obj interface{}) //返回yaml格式 func (c *Context) YAML(code int, obj interface{}) //返回string格式 func (c *Context) String(code int, format string, values ...interface{}) //渲染html模板后返回 func (c *Context) HTML(code int, name string, obj interface{})
示例:
package main import ( "fmt" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.GET("/json", func(c *gin.Context) { c.JSON(200, gin.H{"message": "return json data"}) }) router.GET("/string", func(c *gin.Context) { c.String(200, "message %s", "return string data") }) router.GET("/yaml", func(c *gin.Context) { arr := [][]string{ {"one", "two", "three"}, {"four", "five", "six"}, } c.YAML(200, arr) }) router.GET("/xml", func(c *gin.Context) { person := struct { //聲明一個匿名結構體 Name string Age int }{"Jane", 20} c.XML(200, fmt.Sprintln(person)) }) router.Run() }
狀態碼
注意上面每一個響應中都有一個狀態碼200。
這個狀態碼不僅可以手動指定一個數字,比如200,500,404;也可以使用http包中的狀態碼,語義化的狀態碼更好理解;
下面解釋http協議中所有的狀態碼了。
package http // HTTP status codes as registered with IANA. // See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml const ( StatusContinue = 100 // RFC 7231, 6.2.1 StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 StatusProcessing = 102 // RFC 2518, 10.1 StatusOK = 200 // RFC 7231, 6.3.1 StatusCreated = 201 // RFC 7231, 6.3.2 StatusAccepted = 202 // RFC 7231, 6.3.3 StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4 StatusNoContent = 204 // RFC 7231, 6.3.5 StatusResetContent = 205 // RFC 7231, 6.3.6 StatusPartialContent = 206 // RFC 7233, 4.1 StatusMultiStatus = 207 // RFC 4918, 11.1 StatusAlreadyReported = 208 // RFC 5842, 7.1 StatusIMUsed = 226 // RFC 3229, 10.4.1 StatusMultipleChoices = 300 // RFC 7231, 6.4.1 StatusMovedPermanently = 301 // RFC 7231, 6.4.2 StatusFound = 302 // RFC 7231, 6.4.3 StatusSeeOther = 303 // RFC 7231, 6.4.4 StatusNotModified = 304 // RFC 7232, 4.1 StatusUseProxy = 305 // RFC 7231, 6.4.5 _ = 306 // RFC 7231, 6.4.6 (Unused) StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7 StatusPermanentRedirect = 308 // RFC 7538, 3 StatusBadRequest = 400 // RFC 7231, 6.5.1 StatusUnauthorized = 401 // RFC 7235, 3.1 StatusPaymentRequired = 402 // RFC 7231, 6.5.2 StatusForbidden = 403 // RFC 7231, 6.5.3 StatusNotFound = 404 // RFC 7231, 6.5.4 StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5 StatusNotAcceptable = 406 // RFC 7231, 6.5.6 StatusProxyAuthRequired = 407 // RFC 7235, 3.2 StatusRequestTimeout = 408 // RFC 7231, 6.5.7 StatusConflict = 409 // RFC 7231, 6.5.8 StatusGone = 410 // RFC 7231, 6.5.9 StatusLengthRequired = 411 // RFC 7231, 6.5.10 StatusPreconditionFailed = 412 // RFC 7232, 4.2 StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11 StatusRequestURITooLong = 414 // RFC 7231, 6.5.12 StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13 StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 StatusExpectationFailed = 417 // RFC 7231, 6.5.14 StatusTeapot = 418 // RFC 7168, 2.3.3 StatusUnprocessableEntity = 422 // RFC 4918, 11.2 StatusLocked = 423 // RFC 4918, 11.3 StatusFailedDependency = 424 // RFC 4918, 11.4 StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 StatusPreconditionRequired = 428 // RFC 6585, 3 StatusTooManyRequests = 429 // RFC 6585, 4 StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 StatusInternalServerError = 500 // RFC 7231, 6.6.1 StatusNotImplemented = 501 // RFC 7231, 6.6.2 StatusBadGateway = 502 // RFC 7231, 6.6.3 StatusServiceUnavailable = 503 // RFC 7231, 6.6.4 StatusGatewayTimeout = 504 // RFC 7231, 6.6.5 StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6 StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 StatusInsufficientStorage = 507 // RFC 4918, 11.5 StatusLoopDetected = 508 // RFC 5842, 7.2 StatusNotExtended = 510 // RFC 2774, 7 StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 )
測試,在使用的時候,直接作為上面響應中的參數即可。
router.GET("/json", func(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{"message": "not found"}) //等價於 //c.JSON(404, gin.H{"message": "not found"}) })
綁定路由
gin框架和使用原生http庫綁定路由的形式很相似,但是呢,go語言支持在路由中使用類似於正則表達式的路由,注意這里是類似於正則表達式,因為這里有一定的規則。
獲取URL中的路徑參數
比如:http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wigth=100
上面的這個鏈接中,可以通過向上面講的使用/user/:name/:age/:addr/:sex來分別匹配jane、20、beijing、female。
獲取URL的所有路徑參數
gin框架中如果使用URL傳遞參數,那么在綁定的時候,是這樣的:
/user/:name/:age/:addr/:sex
上面這個path綁定了4個URL參數,分別是name、age、addr、sex
首先gin.Context對象的Params屬性保存着URL中的所有參數:
router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) { //打印URL中所有參數 c.JSON(200, fmt.Sprintln(c.Params)) })
如果請求http://localhost:8080/user/jane/20/beijing/female
得到的輸出如下:
"[{name jane} {age 20} {addr beijing} {sex female}]\n"
注意最后面的那個換行符
獲取URL中的指定路徑參數
使用gin.Context對象的Param(key)方法獲取某一個key的值,方法聲明如下:
router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) { name := c.Param("name") //獲取參數的時候,不要寫name前面的冒號: c.JSON(200, name) })
訪問http://localhost:8080/user/jane/20/beijing/female,輸出"jane"
獲取URL中請求的參數(GET請求)
獲取URL中路徑值和獲取參數不一樣。
比如:http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wight=100
可以使用接下在的方法獲取請求參數id、height、weight的值。
獲取指定參數的只
使用gin.Context.Query(),
//返回URL中key的值 func (c *Context) Query(key string) string
測試:
router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) { id := c.Query("id") height := c.Query("height") weight := c.Query("weight") c.JSON(200, gin.H{"id": id, "height": height, "weight": weight}) })
訪問http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wight=100,輸出內容如下:
{"height":"170","id":"999","weight":"100"}
獲取指定參數的值(帶有默認值的接收)
當然,以前寫php的時候,如果要想判斷是否接收到了某個參數,如果沒有接收到,那么就設置一個默認值,最常用的就是三元運算符,比如下面這樣:
$id = empty($_POST['id']) ? 0 : $_POST['id'];
gin框架當然也想到了這么一點,gin.Context.DefaultQuery()方法,允許你指定接收的參數名,以及沒有接收到該參數值時,設置的默認值,聲明如下:
func (c *Context) DefaultQuery(key, defaultValue string) string
只有當請求沒有攜帶key,那么此時的默認值就會生效。其他情況,默認值不生效。即使URL中的該key的值為空,那么也不會啟用默認值,獲取的值就是空。
注意,這是獲取URL中的參數值。
接收multipart/urlencoded form(POST表單數據)
和獲取URL中的參數很相似:
GET請求中的參數使用Query(key),DefaultQuery(key,default)來獲取
POST請求中的參數使用PostForm(key),DefaultPostForm(key,default)來獲取。
package main import ( "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.POST("/post", func(c *gin.Context) { name := c.PostForm("name") age := c.PostForm("age") sex := c.DefaultPostForm("sex", "male") c.JSON(200, gin.H{"name": name, "age": age, "sex": sex}) }) router.Run() }
注意要想獲取POST方式傳遞的參數,那么綁定的路由監聽方式就必須是router.POST,不能使router.GET。
同時獲取URL中的參數和POST的參數
前提:請求方必須是使用POST方式傳遞,只不過URL中也包含一部分參數而已。
同時,服務器端必須使用router.POST方式來接收,不能使用router.GET方式接收,因為router.GET只能接收GET方式傳遞的數據。
package main import ( "github.com/gin-gonic/gin" ) func main() { router := gin.Default() //注意,這里必須使用router.POST router.POST("/post", func(c *gin.Context) { name := c.PostForm("name") age := c.PostForm("age") sex := c.DefaultPostForm("sex", "male") addr := c.Query("addr") hobby := c.DefaultQuery("hobby", "basketball") c.JSON(200, gin.H{"name": name, "age": age, "sex": sex, "addr": addr, "hobby": hobby}) }) router.Run() }
請求:localhost:8080/post?addr=beijing&hobby=football
同時使用post方式傳遞name=abc&age=21111&sex=female
那么得到的響應就是如下內容:
{"addr":"beijing","age":"21111","hobby":"football","name":"abc","sex":"female"}
接收post發送的單個文件
首先需要注意的是:請求方的method必須是post,Content-Type或者表單中要設成multipart/form-data。
服務器端也要使用post方式接收。
使用gin.Context.FormFile("file")來接收上傳的文件,聲明如下:
func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
示例:
package main import ( "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.POST("/upload", func(c *gin.Context) { file, _ := c.FormFile("myfile") //獲取文件 filename := file.Filename size := file.Size header := file.Header c.JSON(200, gin.H{ "filename": filename, "size": size, "header": header, }) }) router.Run() }
使用瀏覽器發送文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data"> 上傳文件:<input type="file" name="myfile"><br> <input type="submit"> </form> </body> </html>
發送了一個文件后,獲得的響應如下:
{ "filename": "index.html", "header": { "Content-Disposition": [ "form-data; name=\"myfile\"; filename=\"index.html\"" ], "Content-Type": [ "text/html" ] }, "size": 289 }
接收post發送的多個文件
可以按照使用接收單個文件的套路,來處理多個文件。
比如下面這樣:
package main import ( "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.POST("/upload", func(c *gin.Context) { file_1, _ := c.FormFile("myfile_1") file_2, _ := c.FormFile("myfile_2") file_3, _ := c.FormFile("myfile_3") c.IndentedJSON(200, gin.H{ "file_1": file_1.Filename, "file_2": file_2.Filename, "file_3": file_3.Filename, }) }) router.Run() }
html代碼如下:
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data"> 上傳文件一:<input type="file" name="myfile_1"><br> 上傳文件二:<input type="file" name="myfile_2"><br> 上傳文件三:<input type="file" name="myfile_3"><br> <input type="submit"> </form>
發送三個文件,獲得的相應:
{ "file_1": "fire.png", "file_2": "index.html", "file_3": "Unknown.png" }
一次性接收多個文件
前面這種方法雖然能接收很多文件,但是,每一個文件都要有單獨的name,這就存在一個問題,如果上傳的文件有成百上千個,那么就有的忙了。
這里就可以使用gin框架封裝的另外一個接口
組路由
什么叫組路由?為什么要使用組路由?優點?
首先看下面這段代碼:
func main() { router := gin.Default() router.GET("/v1/demo", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v1/demo"}) }) router.GET("/v1/test", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v1/test"}) }) router.GET("/v2/demo", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v2/demo"}) }) router.GET("/v2/test", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v2/test"}) }) router.Run() }
這段代碼一點毛病都沒有,運行也很正常,但是,有個問題,如果v1,v2是API的版本,那么上面這樣寫,是不是不太直觀呀,如果幾百個接口都有v1和v2兩個版本,那這個文件的可讀性太差了。
所以,組路由就派上大用場了。
上面的程序可以改寫下面這樣:
func main() { router := gin.Default() v1 := router.Group("/v1") { v1.GET("/demo", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v1/demo"}) }) v1.GET("/test", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v1/test"}) }) } v2 := router.Group("v2") { v2.GET("/demo", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v2/demo"}) }) v2.GET("/test", func(c *gin.Context) { c.JSON(200, gin.H{"data": "/v2/test"}) }) } router.Run() }
照樣一點毛病都沒有。
使用中間件
之前我們使用的都是router.Default(),其實也是使用了中間件的,比如logger、recover,這是gin框架默認附帶的。
指定訪問日志文件
這個就是類似於apache的訪問日志文件
func main() { //取消控制台中日志的字體顏色 gin.DisableConsoleColor() //創建一個日志文件 access_log, _ := os.Create("access_log.log") gin.DefaultWriter = io.MultiWriter(access_log) router := gin.Default() router.GET("/log", func(c *gin.Context) { c.JSON(200, gin.H{"message": "hello world"}) }) router.Run() }
嘗試請求http://localhost:8080/log,然后查看當前目錄的access_log.log文件。
請求時間 狀態碼 請求處理時間 發起請求的IP 發起請求的方法 請求的路徑 [GIN] 2018/08/02 - 19:33:28 | 200 | 273.678µs | ::1 | GET /log
綁定post參數到模型中
請看下面的代碼,完成一個登錄驗證功能:
func main() { router := gin.Default() router.POST("/login", func(c *gin.Context) { username := c.PostForm("username") password := c.PostForm("password") if username == "abc" && password == "123" { c.JSON(200, gin.H{"message": "welcome"}) } else { c.JSON(401, gin.H{"message": "wrong username or password"}) } }) router.Run(":8080") }
現在gin框架提供另外一種方式,如下:
type Login struct { User string `form:"user" json:"user" binding:"required"` Password string `form:"password" json:"password" binding:"required"` } func main() { router := gin.Default() router.POST("/loginJSON", func(c *gin.Context) { var json Login if err := c.ShouldBindJSON(&json); err == nil { if json.User == "abc" && json.Password == "123" { c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) } } else { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }) router.POST("/loginForm", func(c *gin.Context) { var form Login if err := c.ShouldBind(&form); err == nil { if form.User == "abc" && form.Password == "123" { c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) } else { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) } } else { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }) router.Run() }
首先,綁定兩個路徑分別是loginJSON、loginForm。
對於loginJSON來說,通過gin.Context.ShouldBindJSON(&json),可以將post方式傳遞json格式的username和password分別賦值給json這個結構體中的User和Password屬性,然后在使用該結構體來完成驗證。
比如,請求http://localhost:8080/loginJSON,通過post請求傳遞了{"user":"abc","password":"123"},那么就可以通過驗證,獲得響應結果是:{"status":"you are logged in"},其他的登錄名和密碼都不能通過驗證。
相對於loginJSON來說,loginForm中使用的gin.Context.ShouldBind(&json)是一樣的用法。
綁定GET參數到模型中
從URL中獲取參數綁定到模型使用的是gin.ShouldBindQuery(&json)方法,注意此時綁定路由時,使用GET方法
例子:
type Login struct { User string `form:"user" json:"user" binding:"required"` Password string `form:"password" json:"password" binding:"required"` } func main() { router := gin.Default() router.GET("/login", func(c *gin.Context) { var json Login if err := c.ShouldBindQuery(&json); err == nil { if json.User == "abc" && json.Password == "123" { c.JSON(200, gin.H{"message": "welcome"}) } else { c.JSON(401, gin.H{"message": "wrong username or password"}) } } else { c.JSON(400, gin.H{"error": err.Error()}) } }) router.Run() }
測試:
請求http://localhost:8080/login?user=abc&password=123,攜帶了user和password,返回{"message":"welcome"}
gin.HandlerFunc
為什么突然提到這個,因為,我們一直在用它,比如下面的
router.GET("/demo", func(c *gin.Context) { c.JSON(200, gin.H{"message": "hello world"}) })
綁定的func(c *gin.Context){ code } 就是gin.HandlerFunc,這里是一個匿名函數的形式,其實我們完全可以將它提出來,讓多個路徑共用,比如下面這樣:
//聲明一個gin.HandlerFunc func response(c *gin.Context) { c.JSON(200, gin.H{"message": "hello world"}) } func main() { router := gin.Default() router.GET("/demo", response) router.GET("/test", response) router.GET("/aaaa", response) router.Run() }
上面這個程序執行后,不管是訪問demo、test、aaaa都是使用同一個事件處理程序。
從POST或者GET中獲取數據綁定到模型
如果了解PHP的話,就知道php接收數據有$_GET、$_POST超全局數組,分別用來接收get請求和get請求傳遞的參數。同時還有$_REQUEST超全局數組既可以接收get請求和post請求中的參數(這里其實不是$_GET、$_POST、$_REQUEST來接收,而是將接收到的數據存在這些超全局數組里面)。
其實gin框架中雖然沒有$REQUEST,但是可以使用下面這個程序替代一下,原理就是上面的多個路由綁定同一個事件處理程序:
type Login struct { User string `form:"user" json:"user" binding:"required"` Password string `form:"password" json:"password" binding:"required"` } //聲明一個gin.HandlerFunc func LoginCheck(c *gin.Context) { var json Login var err error if err := c.ShouldBindQuery(&json); err == nil { //嘗試從get請求中獲取參數 if json.User == "abc" && json.Password == "123" { c.JSON(200, gin.H{"message": "login in success by Get method"}) } else { c.JSON(200, gin.H{"message": "login in failed by Get method"}) } } else if err := c.ShouldBind(&json); err == nil { //嘗試從post請求中獲取參數 if json.User == "abc" && json.Password == "123" { c.JSON(200, gin.H{"message": "login in success by POST method"}) } else { c.JSON(200, gin.H{"message": "login in failed by POST method"}) } } if err != nil { //解析請求中的參數失敗 c.JSON(400, gin.H{"message": err.Error()}) } } func main() { router := gin.Default() //對於GET和POST,都嘗試使用同一個事件處理程序 router.GET("login", LoginCheck) router.POST("login", LoginCheck) router.Run() }
接收checkbox的請求參數,綁定到模型中
接收checkbox數據
前面講的使用gin.Context.PostForm(key)接收單個post請求中參數key的值,但是如果傳遞多個同名的參數(checkbox)時,如下:
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data"> <input type="checkbox" name="hobby[]" value="swimming">游泳 <input type="checkbox" name="hobby[]" value="basketball">籃球 <input type="checkbox" name="hobby[]" value="football">足球 <input type="submit"> </form>
那么使用前面的PostForm(key)只能獲取到第一個值swimming。
如果要接受多個hobby,就必須使用gin.Context.PostFormArray("key")來接收即可,向下面這樣:
func main() { router := gin.Default() router.POST("/checkbox", func (c *gin.Context) { data := c.PostFormArray("hobby[]") //注意接收的key是一個數組(后面加[]) c.JSON(200, gin.H{"message": data}) }) router.Run() }
運行測試,全選checkbox,發送post請求后,獲得的相應如下:
{ "message": [ "swimming", "basketball", "football" ] }
綁定到模型中
如果要將收到的checkbox數據綁定到模型中,使用gin框架提供的方法,可以向下面這么做:
type CheckBox struct { //注意如果是checkbox,那么標簽中的key要加[],還有就是屬性一定要大寫(可見性) Hobby []string `form:"hobby[]" json:"hobby[]" binding:"required"` } func main() { router := gin.Default() router.POST("/checkbox", func(c *gin.Context) { var checkbox CheckBox if c.ShouldBind(&checkbox) == nil { c.JSON(200, gin.H{"hobby": checkbox.Hobby}) } else { c.JSON(400, gin.H{"message": "invalid request params"}) } }) router.Run() }
很容易忽略的一點就是結構體中的Hobby屬性一定要大寫,否則因為可見性的規則,小寫的hobby是不能進行模型綁定的,訪問到的也是一個null值。
返回靜態文件
其實在網站訪問過程中,有很多的資源都是靜態的,也就是說,這些靜態資源是可以直接從硬盤上讀取之后返回用戶,不需要服務器端在做多余的處理。
gin框架中提供了三個方法來實現:
func main() { router := gin.Default() //設置靜態文件目錄,如果訪問localhost:8080/assets/test.txt //如果./assets/test.txt文件存在,那么就返回該文件,否則返回404 router.Static("/assets", "./assets") //和上面的功能一樣 router.StaticFS("/my_file", http.Dir("my_file")) //為單個文件綁定路由 //可以通過訪問localhost:8080/family.pic來獲取./pic/family.pic文件 router.StaticFile("/family.pic", "./pic/family.pic") router.Run() }
注意上面的綁定path,使用的相對路徑,不是絕對路徑。