0、GET/POST 請求資料
在 PHP 中,可以直接通過全局變量 $_GET 和 $_POST 快速擷取 GET/POST 請求資料,GET 請求資料主要是 URL 查詢字元串中包含的參數,以前面線上論壇項目的群組詳情頁為例:
http://localhost:8080/thread/read?id=adb02107-d7c3-4f27-4de4-b586f231380e
上述 URL 請求中的 id=adb02107-d7c3-4f27-4de4-b586f231380e 就屬于 GET 請求資料,也就是查詢字元串,而對于使用者登入表單:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yYkRmMyQGMkF2N4IWYmhzN2UTMkZDN3EGM4QDMxEGNi9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
當使用者輸入注冊郵箱和密碼後點選「登入」按鈕,會将輸入框中的資料作為請求實體發送 POST 請求到服務端,執行登入認證,這裡的表單資料就是 POST 請求資料,如果我們檢視表單請求路由的 HTTP 封包:
就會看到表單請求資料,也就是上文提到的 HTTP 請求實體。
當然根據對服務端資源的操作類型不同,可以進一步細分為 POST、PUT、PATCH、DELETE 等包含請求實體的請求方法,為了簡化模型,我們這裡隻讨論 POST 請求,而且其他幾種請求方法也可以統一通過 POST 請求完成,通常隻有在設計遵循 RESTful 風格的 API 接口時,才會嚴格劃分不同的請求方法,關于這一點,後面介紹 REST + JSON 接口時再詳細讨論。
1、Form
Go 語言中擷取使用者請求資料的方式要更複雜一些,Go 也為此提供多個不同的結構體幫助我們讀取不同請求類型的資料,首先,我們可以通過請求對象上的 Form 讀取所有 GET/POST 請求資料,在 handlers/post.go 中新增 EditPost 方法如下:
func EditPost(w http.ResponseWriter, r *http.Request) { r.ParseForm() fmt.Fprintln(w, r.Form)}
需要注意的是,在通過 r.Form 擷取所有請求資料之前,必須要先通過 r.ParseForm() 解析所有請求資料,否則無法擷取資料。
在 routes/web.go 中新增一個路由:
WebRoute{ "UpdatePost", "POST", "/post/edit", handlers.EditPost,},
然後我們重新開機 HTTP 伺服器,在指令行通過 curl 發起一個包含不同類型請求資料的請求:
可以看到,列印結果表明 r.Form 是一個包含所有請求資料的字典類型(map),包含 URL 查詢字元串和 POST 請求資料,這是一個 POST 請求,如果是 GET 請求呢?我們為 EditPost定義一個 GET 請求路由:
WebRoute{ "EditPost", "GET", "/post/edit", handlers.EditPost,},
再重新開機 HTTP 伺服器,發起一個隻包含查詢字元串的 GET 請求(預設是 GET 請求,不需要通過 -X GET 指定):
同樣可以列印出和 POST 請求完全一樣的結果。
是以,request 對象上的 Form 可以擷取所有請求參數,包括查詢字元串和請求實體,并且不限請求類型。如果你想要進一步要擷取指定的參數值,可以以索引方式擷取指定參數對應的值,也可以通過 Form 提供的 Get 方法,就像我們從一個普通字典類型擷取鍵值一樣:
id1 := r.Form["id"]id2 := r.Form.Get("id")fmt.Println(id1)fmt.Println(id2)
隻不過兩者的傳回值類型不一樣,前者是一個字元串切片,後者是一個字元串值:
[1]1
2、PostForm
上面的結果同時傳回了查詢字元串和請求實體,如果隻想擷取請求實體(即 POST 表單中的資料),可以通過 PostForm 實作:
func EditPost(w http.ResponseWriter, r *http.Request) { r.ParseForm() id := r.Form.Get("id") fmt.Println("post id:", id) fmt.Println("form data:", r.PostForm) io.WriteString(w, "表單送出成功")}
這樣一來,就隻能擷取到 POST 資料了:
然後在 HTTP 伺服器日志,可以看到如下答應結果:
可以看到 r.PostForm 傳回的也是字典類型資料,資料格式和 r.Form 完全一緻,并且這次隻包含了 POST 表單請求資料,不包含 URL 查詢字元串,也就是說,通過 r.PostForm 隻能擷取 POST 請求資料(請求實體資料),無法擷取 GET 請求資料(查詢字元串中的資料),你可以再次發起 GET 請求進行驗證:
這個時候,可以看到伺服器列印日志之包含 id 資訊,表單資訊為空:
通過 PostForm 擷取具體參數值的方式和 Form 一樣,這裡就不再贅述了:
title := r.PostForm.Get("title")content := r.PostForm.Get("content")
實際上,我們在前面的線上論壇項目中,就是通過這個方式擷取表單請求資料的:
err := request.ParseForm()user, err := models.UserByEmail(request.PostFormValue("email"))
3、FormValue/PostFormValue
最後,還可以通過 FormValue 和 PostFormValue 擷取使用者請求資料,使用它們的好處是不再需要單獨調用 ParseForm 對表單資料進行解析,不過使用這兩個方法的時候隻能擷取特定請求資料,不能一次擷取所有請求資料:
func EditPost(w http.ResponseWriter, r *http.Request) { fmt.Println("post id:", r.FormValue("id")) fmt.Println("post title:", r.PostFormValue("title")) fmt.Println("post title:", r.PostFormValue("content")) io.WriteString(w, "表單送出成功")}
FormValue/PostFormValue 的差別和 Form/PostForm 一樣,這裡通過命名就可以看出來,前者可以擷取所有 GET/POST 請求資料(即查詢字元串和請求實體),後者隻能擷取 POST 請求實體資料。
注:FormValue/PostFormValue 之是以不用顯式調用 ParseForm 解析請求資料,是因為底層對其進行了封裝,實際上還是要調用這個方法。
4、擷取 JSON 請求資料
上面的示例預設都是基于 HTML 表單請求,對于用戶端送出的 JSON 格式資料,使用ParseForm 是無法解析并擷取資料的,因為 HTML 表單請求資料預設是通過 application/x-www-form-urlencoded 編碼的,而 JSON 請求資料通常是通過 application/json 編碼,ParseForm 隻能解析通過 application/x-www-form-urlencoded 編碼的資料。
對于 JSON 請求資料的解析,目前我們可以通過上篇教程介紹的,讀取完整請求實體并進行 JSON 解碼實作,下面我們改寫 AddPost 方法實作如下:
type Post struct { Title string `json:"title"` Content string `json:"content"`}func AddPost(w http.ResponseWriter, r *http.Request) { len := r.ContentLength // 擷取請求實體長度 body := make([]byte, len) // 建立存放請求實體的位元組切片 r.Body.Read(body) // 調用 Read 方法讀取請求實體并将傳回内容存放到上面建立的位元組切片 // io.WriteString(w, string(body)) post := Post{} json.Unmarshal(body, &post) // 對讀取的 JSON 資料進行解析 fmt.Fprintf(w, "%#v", post) // 格式化輸出結果}
我們将讀取的請求實體資料通過 JSON 解碼映射到 Post 結構體對象并将其輸出到響應結果。
重新開機 HTTP 伺服器,通過 curl 模拟用戶端送出 JSON 請求資料:
我們通過 -H 選項指定請求資料編碼格式為 application/json,然後請求資料調整為 JSON 格式字元串,最後通過輸出結果可以看到在服務端 JSON 請求資料已經可以成功解析并擷取。
5、小結
到這裡,我們已經了解了在 Go Web 程式設計中,常見的使用者請求資料如何解析并擷取(URL 查詢字元串、POST 表單資料、JSON 請求資料),實際上,和 PHP 中的 $_GET 和 $_POST 類似,Go 也是将 HTTP 請求資料映射到請求對象對應的結構體,然後開發者可以從上下文請求對象中解析并讀取這些請求資料,使用這些封裝好的對象的好處是它們屏蔽了底層的細節,統一了資料格式,可以大大提高開發效率,減少不必要的資料格式相容成本。
下篇教程,學院君将給大家介紹如何從表單請求中擷取檔案資料,并實作簡單的檔案上傳功能。
(全文完)