Web應用程式最重要的功能,就是接收來自客戶端(一般是瀏覽器或APP)發起的請求,根據請求方法與型別返回靜態HTML頁面或動態生成客戶端想要的資料,而Go語言中net/http
已經在處理請求資料方面做了很好的封裝,使得用Go程式碼處理Web請求的資料並生成響應資料變得非常簡單,下面一起學習一下吧!
Web請求
我們知道,一個HTTP事務由請求
和響應
構成,這篇文章中,我們單講有關Web請求的部分。
客戶端一般是通過一個URL
向伺服器發起請求,最簡單的比如在瀏覽位址列輸入:juejin.im。
每一個Web請求都包括三個部分:請求行
、請求頭
、請求實體
。
請求方法
請求方法
在請求行
當中,HTTP協議支援多種請求方法(method)
,主要有七種:
GET
,POST
,PUT
,HEADER
,PATCH
,DELETE
,OPTIONS
。
其中Web開發最常見就是GET
方法和POST
方法,使用GET
方法發起的請求,沒有請求實體(body)
,因些請求的資料一般只能通過URL
中的查詢引數(query)
傳給服務端,而使用POST方法的請求則會攜帶請求實體
,在請求實體
中帶有傳給服務端的資料。
Content-Type
Content-Type
是請求頭部(Header)
中一個用於指請求實體
型別的內容頭部,在請求或響應用於指請求實體
到底存放什麼樣的資料,所以只有會推帶請求實體
的方法起作用,如POST
方法。
在一般Web開發中,Content-Type最常用取值有下面四種:
-
application/json
:JSON資料 -
application/x-www-form-urlencoded
:form表單請求使用的型別,在傳送前會編碼所有字元 -
multipart/form-data
:不對字元編碼,一般用於檔案上傳 -
text/html
:一般用於響應中的響應頭,告訴客戶端返回的是HTML文件。
查詢引數(query)
查詢引數是URL中?後面跟著的部分,比如在掘金的搜尋框中輸入:Go,我們會看到瀏覽器的位址列變成:
查詢引數就是指query=Go&type=1
,查詢引數由&分隔開,這是GET方法攜帶資料的方式,Go語言中支援獲取查詢引數部分的引數。
請求實體(body)
請求實體
是一次Web請求中資料攜帶部分,一般只有POST請求才有這個部分,伺服器由Content-Type首部來判斷請求實體
的內容編碼格式,如果想向伺服器傳送大量資料,一般都用POST請求。
Go處理Web請求資料
在Go語言中,使用http.Request
結構來處理http請求的資料,在我們定義處理請求的方法,會傳入http.Request的例項,如下程式碼中request
就是代表一個請求的例項。
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
//使用request可以獲取http請求的資料
})
複製程式碼
在golang官方文件中,可以看到http.Request的包外可訪問方法列表:
下面是http.Request公開可訪問的欄位
type Request struct {
Method string //方法:POST,GET...
URL *url.URL //URL結構體
Proto string // 協議:"HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header Header //頭部資訊
Body io.ReadCloser //請求實體
GetBody func() (io.ReadCloser, error) // Go 1.8
ContentLength int64 //首部:Content-Length
TransferEncoding []string
Close bool //是否已關閉
Host string //首部Host
Form url.Values //引數查詢的資料
PostForm url.Values // application/x-www-form-urlencoded型別的body解碼後的資料
MultipartForm *multipart.Form //檔案上傳時的資料
Trailer Header
RemoteAddr string //請求地址
RequestURI string //請求的url地址
TLS *tls.ConnectionState
Cancel <-chan struct{} //
Response *Response // 響應資料
}
複製程式碼
獲得請求頭(Header)
對於常用的請求頭部資訊,http.Request結構有對應的欄位和方法,如下所示:
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
request.RemoteAddr
request.RequestURI
request.ContentLength
request.Proto
request.Method
request.Referer()
request.UserAgent()
})
複製程式碼
當然,其他的首頁,可以通過request.Header欄位來獲取,request.Header的定義如下所示:
type Header map[string][]string
複製程式碼
也就是說,request.Header是一個的型別是map,另外request.Header也提供相應的方法,如下所示:
也就是說,我們除了使用上面的方法獲取頭部資訊外,也可以使用request.Header來獲取,示例:
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
request.Header.Get("Content-Type")//返回的是string
request.Header["Content-Type"] //返回的是[]string
})
複製程式碼
獲取查詢引數(Query)
如何獲取查詢引數(url中?後面使用&分隔的部分)呢?可以使用request.FormValue(key)方法獲取查詢引數,其中key為引數的名稱,程式碼如下:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
username := request.FormValue("username")
gender := request.FormValue("gender")
fmt.Fprintln(writer,fmt.Sprintf("使用者名稱:%s,性別:%s",username,gender))
})
fmt.Println(http.ListenAndServe(":8080",nil))
}
複製程式碼
在Postman輸入http://localhost:8080/hello?username=test&gender=男
獲取表單資訊(Form)
我們說獲取表單資訊,一般是指獲取Content-Type是application/x-www-form-urlencoded
或multipart/form-data
時,請求實體
中的資料,如果你有做傳統網頁中的表單提交資料的經歷,相信對這兩種提交資料的方式應該是熟悉的,而multipart/form-data
一般是用來上傳檔案的。
application/x-www-form-urlencoded
獲取Content-Type為application/x-www-form-urlencoded時提交上來的資料,可以使用request.PostForm欄位request.Form和request.PostFormValue(key)方法獲取,但必須先呼叫request.ParseForm()將資料寫入request.PostForm欄位中。
步驟為:
- 使用request.ParseForm()函式解析body引數,這時會將引數寫入Form欄位和PostForm欄位當中。
- 使用request.Form、request.PostForm或request.PostFormValue(key)都可以獲取
注意,request.Form和request.PostForm的型別url.Values,結構定義如下:
type Values map[string][]string
複製程式碼
示例如下:
package main
import (
"fmt"
"net/http"
)
func main(){
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
err := request.ParseForm()
if err != nil{
fmt.Fprintln(writer,"解析錯誤")
}
username1 := request.PostForm["username"][0]
username2 := request.PostFormValue("username")
username3 := request.Form["username"][0]
fmt.Fprintln(writer,fmt.Sprintf("username1:%s,username2:%s,usernam3:%s",username1,username2,username3))
})
fmt.Println(http.ListenAndServe(":8080",nil))
}
複製程式碼
使用Postman輸入http://localhost:8080/hello,結果如下:
multipart/form-data
獲取Content-Type
為multipart/form-data
時提交上來的資料,步驟如下:
- 使用request.ParseMultipartForm(maxMemory),解析引數,將引數寫入到MultipartForm欄位當中,其中maxMemory為上傳檔案最大記憶體。
- 使用request.FormFile(檔案域),可以獲取上傳的檔案物件:multipart.File
- 除了檔案域,其中引數可以從request.PostForm欄位獲取,注意,此時不需要再呼叫request.ParseForm()了。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/upload", func(writer http.ResponseWriter, request *http.Request) {
err := request.ParseMultipartForm(32 << 20)
if err != nil {
fmt.Fprintln(writer,"檔案上傳錯誤")
return
}
fmt.Println(request.FormFile("file"))
})
fmt.Println(http.ListenAndServe(":8080",nil))
}
複製程式碼
總結
上面簡單地介紹了使用Go語言如何獲取http請求提交上來的資料,重點為獲了表單資料,但其實在現在前後端分離開發趨勢和APP開發中,Content-Type指application/json是更常見的資料提交方式,以後有空可以學一下。