回顧
上一節我們用Gin框架快速搭建了一個GET請求的介面,今天來學習路由和引數的獲取,
請求動詞
熟悉RESTful的同學應該知道,RESTful是網路應用程式的一種設計風格和開發方式,每一個URI代表一種資源,客戶端通過POST、DELETE、PUT、GET四種請求方式來對資源做增刪改查的操作,
同樣的,Gin框架給我們提供的除這4種動詞外,還有PATCH、OPTION、HEAD等,詳細內容可以查看rentergroup.go檔案的IRoutes介面,
type IRoutes interface {
Use(...HandlerFunc) IRoutes
Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes
StaticFile(string, string) IRoutes
Static(string, string) IRoutes
StaticFS(string, http.FileSystem) IRoutes
}
因為RenterGroup實作了IRoutes定義的所有請求動詞,而且gin.Default回傳的Engine型別繼承了RenterGroup,所以使用起來非常簡單,只需要通過gin.Default實體化物件,接下來所有的路由操作都通過該物件使用即可,

func main() {
router := gin.Default()
router.POST("/article", func(c *gin.Context) {
c.String(200, "article post")
})
router.DELETE("/article", func(c *gin.Context) {
c.String(200, "article delete")
})
router.PUT("/article", func(c *gin.Context) {
c.String(200, "article put")
})
router.GET("/article", func(c *gin.Context) {
c.String(200, "article get")
})
router.Run()
}
請求動詞的第一個引數是請求路徑,第二個引數是用于邏輯處理的函式,可以是匿名的或是其他地方定義的函式名,不同的請求動詞可以定義相同的路徑,只需要切換動詞就可以進入對應的處理邏輯,
curl -X PUT http://localhost:8080/article
curl -X POST http://localhost:8080/article
curl -X GET http://localhost:8080/article
curl -X DELETE http://localhost:8080/article
路由引數
GET請求有兩種,一種是在URL后加上?name=pingye,這種是有引數名的,另一種是在路徑中直接加上引數值/article/1,這種沒有引數名,需要在代碼中決議引數,
protocol://hostname:[port]/path/[query]#fragment
我們先來看路由攜帶引數值的玩法,這里有一道題,怎么利用Gin獲取下面鏈接的引數值1,

實作方式非常簡單,只需要在路由中設定好占位符:id,冒號為占位符的標志,冒號后面的引數名可以自定義,Gin會將路由與請求地址進行匹配,若匹配成功會將1賦值為占位符:id,只需呼叫c.Param就可以獲取id的值,
router.GET("/article/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, id)
})
但是,:id占位符會存在一個問題,如果id引數值傳空就會有404的錯誤提示,

于是Gin提供了另一種占位符*id,使用它就可以達到取空值的目的,
router.GET("/article/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, id)
})
普通引數
除了路由攜帶引數值外,接下來看比較傳統的GET傳參方式,
http://localhost:8080/welcome?firstname=Jane&lastname=Doe
可以通過c.Query或c.DefaultQuery方法獲取問號后的引數,
router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "pingyeaa")
lastname := c.Query("lastname")
c.String(200, firstname+" "+lastname)
})
這兩者最終都呼叫了GetQuery方法,唯一的區別是DefaultQuery做了默認值處理,
func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok {
return value
}
return defaultValue
}
func (c *Context) Query(key string) string {
value, _ := c.GetQuery(key)
return value
}
表單引數
從HTML提交過來的表單form內容同樣也可以輕松獲取,
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
curl -d "message=pingye" http://localhost:8080/form_post
{"message":"pingye","nick":"anonymous","status":"posted"}
陣列型別引數
有時候(例如復選框)前端頁面會傳來陣列型別的值,這種型別name相同,但存盤的內容不同,
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded
依然是一個QueryMap方法就搞定,該方法默認回傳map型別,
router.GET("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
c.String(200, ids["a"]+" "+ids["b"])
})
curl http://localhost:8080/post?ids[a]=pingye&ids[b]=hehe
pingye hehe
檔案上傳
一般情況下,檔案上傳會由前端直接傳給云存盤服務商,比如阿里云、七牛云等,比較少的場景會傳給自己的服務器,為了避免書到用時方恨少的情況發生,我們來了解一下,
Gin提供了FormFile方法獲取檔案流,這個方法回傳了一個FileHeader型別的變數,可以呼叫Filename屬性來查看檔案名,
type FileHeader struct {
Filename string
Header textproto.MIMEHeader
Size int64
content []byte
tmpfile string
}
router.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.String(200, file.Filename)
})
通過curl請求介面,可以看到輕松獲取檔案名稱,
curl -X POST http://localhost:8080/upload \
-F "file=@/Users/enoch/Downloads/IMG_9216.JPG" \
-H "Content-Type: multipart/form-data"
IMG_9216.JPG
當然不止可以拿到檔案名,我們還可以使用SaveUploadedFile方法將檔案保存到某個地方,檔案保存時要確保有目標目錄的操作權限,
router.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.String(200, file.Filename)
err := c.SaveUploadedFile(file, "/Users/enoch/Desktop/ab.png")
if err != nil {
c.String(500, err.Error())
}
})
路由分組
當介面發生重大變更(比如入參出參)時,考慮到向下兼容,一般會新增一個介面,但是又希望新介面的名稱顯而易見地看出是老介面的升級版,那么就可以在介面名前加上版本號v1/article這種形式,
v1 := r.Group("v1")
{
v1.POST("/login", func(c *gin.Context) {
c.String(200, "v1/login")
})
v1.POST("/submit", func(c *gin.Context) {
c.String(200, "v1/submit")
})
}
v2 := r.Group("v2")
{
v2.POST("/login", func(c *gin.Context) {
c.String(200, "v2/login")
})
v2.POST("/submit", func(c *gin.Context) {
c.String(200, "v2/submit")
})
}
curl -X POST http://localhost:8080/v1/login
curl -X POST http://localhost:8080/v2/login
Go語言庫代碼示例,歡迎star
https://github.com/pingyeaa/golang-examples
感謝大家的觀看,如果覺得文章對你有所幫助,歡迎關注公眾號「平也」,聚焦Go語言與技術原理,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/27372.html
標籤:Go
