1. 程式人生 > >01 . Go框架之Gin框架從入門到熟悉(路由和上傳檔案)

01 . Go框架之Gin框架從入門到熟悉(路由和上傳檔案)

#### Gin框架簡介 > Gin是使用Go/Golang語言實現的HTTP Web框架, 介面簡潔, 效能極高,截止1.4.0版本,包含測試程式碼,僅14K, 其中測試程式碼9K, 也就是說測試原始碼僅5k左右, 具有類似Martini的API, 效能更高-快40倍. ##### Gin特性 ```go /* 快速: 路由不使用反射,基於Radix樹,記憶體佔用少 中介軟體: HTTP請求,先經過一系列中介軟體和最終操作來處理,例如: Logger, Authorization,GZIP等, 這個特性和NodeJs的Koa框架很像, 中介軟體機制也極大的提高了框架的可擴充套件性. Cr 異常處理: 服務始終可用, 不會宕機,Gin可以捕獲panic,並恢復,而且極為便利的機制處理HTTP請求過程中發生的錯誤. JSON: Gin可以解析並驗證請求的JSON, 這個特性對於Restful API的開發尤其有用. 路由分組: 例如需要授權和不需要授權的API分組,不同版本的API分組. 而且分組可巢狀,且效能不受影響. 渲染內建: 原生支援JSON, XML和HTML的渲染. */ ``` ##### 安裝Gin ```go go get -u -v github.com/gin-gonic/gin /* -v:打印出被構建的程式碼包的名字 -u:已存在相關的程式碼包,強行更新程式碼包及其依賴包 */ ``` ##### 第一個Gin程式 ```go package main import "github.com/gin-gonic/gin" func main() { // 建立一個預設的路由引擎 r := gin.Default() // GET: 請求方式: /hello: 請求的路徑 // 當客戶端以GET的方法請求/hello路徑時,會執行後面的匿名函式 r.GET("/hello", func(c *gin.Context) { // c.JSON: 返回JSON格式的資料 c.JSON(200,gin.H{ "message": "Hello World", }) }) // 啟動HTTP服務,預設在0.0.0.0:8080啟動服務 r.Run() } /* 1. 首先我們使用gin.Default()生成了一個例項,這個例項即WSGI應用程式. 2. 接下來, 我們使用r.Get("/",...)聲明瞭一個路由,告訴Gin什麼樣的URL能觸發傳入的函式, 這個函式返回我們想要顯示在使用者瀏覽器中的資訊. 3. 最後用r.Run()函式讓應用執行在本地伺服器上,預設監聽埠是_8080_, 可以傳入引數, 例如: r.Run(":9999")即執行在9999埠. */ ``` #### 路由 路由方法有GET, **POST, PUT, PATCH, DELETE** 和 **OPTIONS**,還有**Any**,可匹配以上任意型別的請求 ##### 無引數 ```go r.GET("/", func(c *gin.Context) { c.String(http.StatusOK,"wunai") }) /* curl http://127.0.0.1:8080 wunai */ ``` ##### 解析路徑引數 > 有時候我們需要動態的路由,如/user/:name, 通過呼叫不同的url來傳入不同的Name, /user/:name/*role, *代表可選 ```go // 匹配/user/youmen r.GET("/user/:name", func(c *gin.Context) { name := c.Param("name") c.String(http.StatusOK,"Hello %s",name) }) /* curl http://127.0.0.1:8080/user/youmen Hello youmen */ ``` ##### 獲取Query引數 ```go // 匹配users?name=xxx&role=xxx, role可選 r.GET("/users", func(c *gin.Context) { name := c.Query("name") role := c.DefaultQuery("role","teacher") c.String(http.StatusOK,"%s is a %s",name,role) }) /* curl http://127.0.0.1:8080/users?name=youmen&role=student youmen is a student */ ``` ##### http常見傳輸格式 ```go /* application/json application/x-www-form-urlencoded application/xml multipart/form-data 表單引數可以通過PostForm()方法獲取,該方法預設解析的是x-www-form-urlencoded或from-data格式的引數 */ ``` ##### 獲取POST引數 ```go // POST r.POST("/form", func(c *gin.Context) { username := c.PostForm("username") password := c.DefaultPostForm("password","123") // 可設定預設值 c.JSON(http.StatusOK,gin.H{ "username":username, "password":password, }) }) /* curl http://localhost:8080/form -X POST -d 'username=youmen&password=1234' {"password":"1234","username":"youmen"}% */ ``` `Example2` `gin_demo1.go` ```go package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func main() { // 建立一個預設的路由引擎 r := gin.Default() // GET: 請求方式: /hello: 請求的路徑 // 當客戶端以GET的方法請求/hello路徑時,會執行後面的匿名函式 r.GET("/hello", func(c *gin.Context) { // c.JSON: 返回JSON格式的資料 c.JSON(200, gin.H{ "message": "Hello World", }) }) r.POST("/form", func(c *gin.Context) { // 表單引數設定預設值 type1 := c.DefaultPostForm("type","alert") // 接受其他的 username := c.PostForm("username") password := c.PostForm("password") // 多選框 hobbys := c.PostFormArray("hobby") c.String(http.StatusOK,fmt.Sprintf("type is %s, username is %s, password is %s, habbys is %v", type1,username,password,hobbys)) }) // 啟動HTTP服務,預設在0.0.0.0:8080啟動服務 r.Run() } ``` `register.html` ```html Title
使用者名稱: 密碼: 跑步 舉重 金錢 ``` ##### Query和POST混合引數 ```go // GET和POST混合 r.POST("/posts", func(c *gin.Context) { id := c.Query("id") page := c.DefaultQuery("page", "0") username := c.PostForm("username") password := c.DefaultPostForm("username", "0000") c.JSON(http.StatusOK, gin.H{ "id": id, "page": page, "username": username, "password": password, }) }) /* curl "http://localhost:8080/posts?id=9876&page=7" -X POST -d 'username=geektutu&password=1234' {"id":"9876","page":"7","password":"geektutu","username":"geektutu"}% */ ``` ##### Map引數(字典引數) ```go // Map引數(字典引數) r.POST("/post", func(c *gin.Context) { ids := c.QueryMap("ids") names := c.PostFormMap("names") c.JSON(http.StatusOK,gin.H{ "ids": ids, "names": names, }) }) /* curl -g "http://localhost:8080/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David' {"ids":{"Jack":"001","Tom":"002"},"names":{"a":"Sam","b":"David"}}% */ ``` ##### 重定向(Redirect) ```go r.GET("/redirect", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently,"/index") }) r.GET("/index", func(c *gin.Context) { c.Request.URL.Path = "/" r.HandleContext(c) }) /* curl http://127.0.0.1:8080/redirect -i HTTP/1.1 301 Moved Permanently Content-Type: text/html; charset=utf-8 Location: /index Date: Tue, 27 Oct 2020 07:40:25 GMT Content-Length: 41 */ ``` ##### 分組路由 >
如果有一組路由,字首都是`/api/v1`開頭,是否每個路由都需要加上`/api/v1`這個字首呢?答案是不需要,分組路由可以解決這個問題。利用分組路由還可以更好地實現許可權控制,例如將需要登入鑑權的路由放到同一分組中去,簡化許可權控制。 ```go // group routes 分組路由 defaultHandler := func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "path": c.FullPath(), }) } // group: v1 v1 := r.Group("/v1") { v1.GET("/posts", defaultHandler) v1.GET("/series", defaultHandler) } // group: v2 v2 := r.Group("/v2") { v2.GET("/posts", defaultHandler) v2.GET("/series", defaultHandler) } /* curl http://localhost:8080/v1/posts {"path":"/v1/posts"} curl http://localhost:8080/v2/posts {"path":"/v2/posts"}