# Day21 Golang 網頁框架 gin 使用教學(GET、POST、ANY) gin,今天再喝一杯吧。 因為我們的目標是網頁伺服器,果不其然的要從網頁開始講起。 ## 網頁請求方法 HTTP Method HTTP Method 分成了八種`GET`、`POST`、`DELETE`、`PUT`、`HEAD`、`TRACE`、`CONNECT`、`OPTIONS`,但在這我們只會簡介常用的`GET`跟`POST`。 * `GET`:參數都放在網址裡,很不安全(要是參數是登入時的帳號、密碼,就會容易外洩),但方便開發的時候測試。 * `POST`:參數不在網址裡,比較安全的作法。 --- ## Json格式 `Json` (JavaScript Object Notation) 是一種資料格式,由各種 `陣列(Array)`以及`鍵值(Key-Value)`所組成的結構話格式。 `[]`是陣列形式,裡面可以放各種物件 `{}`是鍵值形式,裡面可以放各種物件 兩種形式可以交錯使用,你中有我我中有你 --- ## Get方法 handler 以下使用gin不同的兩種方式(`Context.Data`,`Context.JSON`)來達成 返回json資料格式的效果: ```go func main() { router := gin.Default() router.GET("/json", returnJson) router.GET("/json2", returnJson2) router.Run(":80") } func returnJson(c *gin.Context) { m := map[string]string{"status": "ok"} j, _ := json.Marshal(m) c.Data(http.StatusOK, "application/json", j) } func returnJson2(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "狀態": "ok", }) } ``` ![json](https://i.imgur.com/pToBpRD.png) (在這推薦安裝Chrome擴充套件 [**JSON Viewer Pro**](https://chrome.google.com/webstore/detail/json-viewer-pro/eifflpmocdbdmepbjaopkkhbfmdgijcc) 或其他款可以在瀏覽器上方便看`json`檔案的套件,畫面看起來更舒適) ![json2](https://i.imgur.com/Axx40MW.png) 另外,可以透過Chrome開發者工具 看到`Content-Type`為剛剛所設定的`application/json` ![Content-Type](https://i.imgur.com/iZJ15sa.png) ### Struct 結構裡的tag 可以直接在`struct`中加入`json tag`,讓gin返回`json`格式的時候自動對應轉換。 (當然也有支援`xml`等格式,甚至可以自訂格式) ```go func returnJson3(c *gin.Context) { type Result struct { Status string `json:"status"` Message string `json:"message"` } var result = Result{ Status: "OK", Message: "This is Json", } c.JSON(http.StatusOK, result) } ``` [**詳見Day22-賦予API返回值的意義**](https://ithelp.ithome.com.tw/articles/10245889) #### 【sturct 坑】 記得 struct中的 Status、Message要字首大寫,**要對外暴露給gin套件取用** --- ## GET 接收參數 `GET Method` 是以**網址帶參數**的方式進行參數傳遞,可分成以下兩種: * `查詢參數(Query Params)` * `路徑參數(Path Params)` #### 查詢參數(Query Params) > **127.0.0.1?`user`=`id`** > 參數名稱為`user` > 其中的`id`就是使用者的ID。 >> 127.0.0.1?user=Jack 為什麼叫作**查詢參數**、或稱作Query String呢? 因為他使用`?`問號來 Query參數 能以這方法夾帶**表單參數(HTML Form)**,但傳遞表單時使用POST方法更為安全。 ```go func main() { router := gin.Default() router.GET("/para1", para1) router.Run(":80") } func para1(c *gin.Context) { // input := c.DefaultQuery("input", "使用者沒有任何輸入。") // 使用者沒有輸入參數時 可設定預設值 input := c.Query("input") msg := []byte("您輸入的文字為: \n" + input) // 純文字(text/plain)中的換行是\n,網頁格式(html)中的換行才是<br /> c.Data(http.StatusOK, "text/plain; charset=utf-8;", msg) // 如果沒有指定文字編碼、拿掉`charset=utf-8;`的話,中文會變亂碼。 } ``` 成功執行的話,點開後會出現以下結果: http://127.0.0.1/para1?input=%E5%93%88%E5%93%88%E6%98%AF%E6%88%91%E5%95%A6 ![para1](https://i.imgur.com/Qrhaj8I.png) #### 路徑參數(Path Params) **路徑即是參數**,能以這種方式夾帶參數: > **127.0.0.1/`user`/`id`** > 其中的`user`是參數名稱,可代表使用者頁面, > `id`就是使用者的ID。 >> 127.0.0.1/user/Jack ```go func main() { router := gin.Default() router.GET("/para2/:input", para2) router.Run(":80") } func para2(c *gin.Context) { msg := c.Param("input") c.String(http.StatusOK, "您輸入的文字為: \n%s", msg) // 也可使用 `c.String`返回。第二個參數為組合樣式format // c.String(http.StatusOK, msg) // 如果沒有組合樣式,可直接輸入字串 } ``` 成功執行的話,點開後會出現以下結果: http://127.0.0.1/para2/%E5%93%88%E5%93%88%E6%98%AF%E6%88%91%E5%95%A6 ![para2](https://i.imgur.com/MaCE4lF.png) ## POST接收參數 `post`有寄信、發布訊息的意思 ```go func main() { router := gin.Default() router.RedirectFixedPath = true router.POST("/post", post) router.Run(":80") } func post(c *gin.Context) { //msg := c.PostForm("input") msg := c.DefaultPostForm("input", "表單沒有input。") // 沒有輸入參數時 可設定預設值 c.String(http.StatusOK, "您輸入的文字為: \n%s", msg) } ``` 由於`POST Method`參數都隱藏起來了,並非像`GET`在網址列填入參數就能達到目的, 開瀏覽器難以測試。在這邊我們會使用到[**Postman**](https://www.postman.com/)工具來做測試。 測試時選擇`POST方法` ![POST](https://i.imgur.com/xxvzZRs.png) 選擇`Body`,格式選擇`form-data`, 並且填入表單的`Key、Value` ![Form](https://i.imgur.com/8IM31Ms.png) --- ## Any `router.Any`是任何方法都能夠 handle 的。 > Any registers a route that matches all the HTTP methods. > 包含 GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE. ```go func main() { router := gin.Default() router.Any("/any", any) router.Run(":80") } func any(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "ok", }) } ``` --- gin預設是**大小寫敏感(case sensitivity)**的,只要對應的`URI`字符大小寫不同即視為不同, 這在一些使用情境下非常的不便。 ## gin 自動修正路徑 加上這行 重導向正確的URI,讓大小寫通吃。 router.RedirectFixedPath = true --- 用parameter的方式帶入陣列參數時 可用以下兩種方式帶入 `http://url/?a=123&a=456` `http://url/?a=123,456`