快速上手GO的net/http包
針對(duì)GO中net/http包的學(xué)習(xí)筆記
基礎(chǔ)快速了解
創(chuàng)建簡(jiǎn)單的GOHTTP服務(wù)
func main() { http.HandleFunc("/hello", sayHello) http.ListenAndServe(":8080", nil) //創(chuàng)建基本服務(wù) } func sayHello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, World!")) }
訪問8080/hello進(jìn)行測(cè)試
Handler接口定義:(這部分后面又詳細(xì)解釋)
type Handler interface { ServeHTTP(ResponseWriter, *Request) } //只要有ServeHTTP方法就行
可以自己實(shí)現(xiàn)這個(gè)接口
同時(shí)http提供了handlerFunc結(jié)構(gòu)體
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } //本質(zhì)上就是調(diào)用自身,因?yàn)橐彩且粋€(gè)函數(shù),不過serveHTTP的內(nèi)容自己可以定義改動(dòng)
和之前的HandleFunc區(qū)分,HandleFunc是用來給不同路徑綁定方法的,少一個(gè)r
那么只要滿足Handlerfunc 的簽名形式,就可以進(jìn)行類型轉(zhuǎn)換
- 類型轉(zhuǎn)換的正常的理解例子
type yes func(int, int) int func (y yes) add(a, b int) int { return a + b } func multiply(a, b int) int { fmt.Println(a * b) return a * b } func main() { multiply(1, 2) //2 ans := yes(multiply) //將multiply進(jìn)行轉(zhuǎn)換 res := ans.add(1, 2) fmt.Println(res) //3 }
http.HandleFunc("/hello", hello)
這個(gè)后面的簽名只要是 func(ResponseWriter, *Request)
就可以了
但是
http.ListenAndServe(":8080", referer)
這個(gè)后面的函數(shù)需要是滿足Handler接口,有serveHTTP方法
嘗試搭建檢測(cè)是在query中有name = red
即http://localhost:8080/hello?name=red
發(fā)現(xiàn)會(huì)有重復(fù)覆蓋路由的問題,因?yàn)閘istenandServe會(huì)攔截所有的路由,后面再解決
type CheckQueryName struct { wantname string handler http.Handler } func (this *CheckQueryName) ServeHTTP(w http.ResponseWriter, r *http.Request) { queryParams := r.URL.Query() //獲取get請(qǐng)求的query name := queryParams.Get("name") if name == "red" { this.handler.ServeHTTP(w, r) //其實(shí)就是調(diào)用本身,下面變?yōu)閏heckforname了 } else { w.Write([]byte("not this name")) } } func checkforname(w http.ResponseWriter, r *http.Request) { w.Write([]byte("check is ok")) } func hello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello")) } func main() { thecheck := &CheckQueryName{ //用&因?yàn)閟erveHTTP方法定義在指針接收器上 wantname: "red", handler: http.HandlerFunc(checkforname), } http.HandleFunc("/hello", hello) //滿足func(ResponseWriter, *Request)簽名就可以 http.ListenAndServe(":8080", thecheck) //直接監(jiān)視8080的所有端口,攔截所有路由 }
編寫簡(jiǎn)單的GET請(qǐng)求客戶端
利用defaultclient或者自己定義client都可以
func main() { resp, err := http.DefaultClient.Get("https://api.github.com") if err != nil { panic(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } fmt.Println(string(body)) time.Sleep(time.Second * 2) //等待獲取請(qǐng)求 }
但是如果把網(wǎng)址換成baidu.com就會(huì)獲取不到,這是因?yàn)檗D(zhuǎn)發(fā),以及沒有User-agent的問題
編寫自定義的GET請(qǐng)求客戶端
利用http.Client可以進(jìn)行自定義
func main() { client := &http.Client{ // 允許重定向 CheckRedirect: func(req *http.Request, via []*http.Request) error { return nil }, } req, err := http.NewRequest("GET", "https://www.baidu.com", nil) if err != nil { panic(err) } // 添加請(qǐng)求頭 req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36") resp, err := client.Do(req) //do執(zhí)行HTTP請(qǐng)求的整個(gè)周期包括請(qǐng)求準(zhǔn)備,建立連接,發(fā)送請(qǐng)求,請(qǐng)求重定向,接收響應(yīng)等等 defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } fmt.Println(string(body)) time.Sleep(time.Second * 2) }
發(fā)現(xiàn)可以接受到baidu的網(wǎng)頁html信息
編寫默認(rèn)的post請(qǐng)求客戶端
func main() { postData := strings.NewReader(`{"name": "張三", "age": 25}`) resp, err := http.DefaultClient.Post( "http://localhost:8080/users", "application/json", postData, ) if err != nil { fmt.Printf("POST請(qǐng)求失敗: %v\n", err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Printf("POST響應(yīng): %s\n", string(body)) }
string.NewReader是一種方法將格式改為io reader可以讀取的形式,接收的話也可以postData.Read讀取。
這一類的方式比較多,不一一匯總
對(duì)應(yīng)的server.go (需要在終端中g(shù)o run server.go )兩個(gè)終端分別運(yùn)行服務(wù)端,客戶端
func main() { // 處理 /users 路徑的 POST 請(qǐng)求 http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { // 只允許 POST 方法 if r.Method != http.MethodPost { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "只支持 POST 方法") return } // 讀取請(qǐng)求體 body, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "讀取請(qǐng)求失敗: %v", err) return } defer r.Body.Close() // 解析 JSON 數(shù)據(jù),放入user中 var user User if err := json.Unmarshal(body, &user); err != nil { w.WriteHeader(http.StatusBadRequest) //寫入狀態(tài)碼 fmt.Fprintf(w, "JSON 解析失敗: %v", err) return } // 設(shè)置響應(yīng)頭 w.Header().Set("Content-Type", "application/json") // 構(gòu)造響應(yīng)數(shù)據(jù) response := map[string]interface{}{ "message": "success", "data": map[string]interface{}{ "name": user.Name, "age": user.Age, }, } // 返回 JSON 響應(yīng) json.NewEncoder(w).Encode(response) //將 response 對(duì)象轉(zhuǎn)換為 JSON 格式并寫入響應(yīng) //等價(jià)于: // jsonData, err := json.Marshal(response) // if err != nil { // w.WriteHeader(http.StatusInternalServerError) // return // } // w.Write(jsonData) }) // 啟動(dòng)服務(wù)器 fmt.Println("服務(wù)器啟動(dòng)在 :8080 端口...") if err := http.ListenAndServe(":8080", nil); err != nil { panic(err) } }
多路復(fù)用器
DefaultServeMux一般不會(huì)使用,因?yàn)闀?huì)有沖突等等問題,所以一般用NewServeMux直接創(chuàng)建
type apiHandler struct{} func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, `{"message": "API response"}`) } func main() { mux := http.NewServeMux() mux.Handle("/api/", apiHandler{}) //多引入結(jié)構(gòu)體,后面會(huì)知道有好處 // mux.HandleFunc("/api/", func(w http.ResponseWriter, req *http.Request) { // w.Header().Set("Content-Type", "application/json") // fmt.Fprintf(w, `{"message": "API response from HandleFunc"}`) // }) //和上面等效 mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { if req.URL.Path != "/" { http.NotFound(w, req) return } fmt.Fprintf(w, "Welcome to the home page!") }) fmt.Println("Server running on :8080") http.ListenAndServe(":8080", mux) //用server:= &http.Server創(chuàng)建地址和handler,然后server.ListenAndServe也是一種表現(xiàn)方式 }
mux和http.HandleFunc()的區(qū)別:
mux 可以創(chuàng)建多個(gè)路由器實(shí)例,用于不同的目的。同時(shí)可以為不同的路由器配置不同的中間件(用著先)
第三方有庫httprouter,比如可以解決url不能是變量代表的問題,了解就行
更多都是使用restful API進(jìn)行開發(fā)的~目前的了解有個(gè)概念就行
處理器函數(shù)
Handle
注冊(cè)處理器過程中調(diào)用的函數(shù):Handle
type username struct { name string } func (this *username) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%s", this.name) } func main() { mux := http.NewServeMux() mux.Handle("/jack", &username{name: "jack"}) //會(huì)調(diào)用對(duì)應(yīng)的serveHTTP方法 mux.Handle("/lily", &username{name: "lily"}) //可以為不同的路徑使用相同的處理器結(jié)構(gòu)體,但傳入不同的參數(shù) //這就是比用handleFunc()更靈活的地方 server := &http.Server{ Addr: ":8080", Handler: mux, } if err := server.ListenAndServe(); err != nil { //防止錯(cuò)誤 panic(err) } }
HandlleFunc
處理器函數(shù):HandleFunc
(注意不是HandlerFunc,沒有r !! HandleFunc 是處理器函數(shù))
之前已經(jīng)學(xué)習(xí)過,定義就是func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
深入源代碼會(huì)發(fā)現(xiàn)內(nèi)部也是借助serveMux對(duì)象,從而實(shí)現(xiàn)了Handler的ServeHTTP()方法的
Handler
Handler就是處理器接口,實(shí)現(xiàn)ServeHTTP方法的,之前展示過
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
HandlerFunc
HandlerFunc是結(jié)構(gòu)體,用于實(shí)現(xiàn)接口的
定義:
type HandlerFunc func(ResponseWriter, *Request)
用于連接處理器Handle和處理器函數(shù)HandleFunc,它實(shí)現(xiàn)了 Handler 接口,使得函數(shù)可以直接當(dāng)作處理器使用:
- 理解“連接連接處理器Handle和處理器函數(shù)HandleFunc”:(之前也學(xué)過)
// 方式一:普通函數(shù) func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello!") } // 注冊(cè)方式一:將函數(shù)轉(zhuǎn)換為 HandlerFunc http.Handle("/hello", http.HandlerFunc(hello)) // 方式二:直接使用 HandleFunc http.HandleFunc("/hello", hello)
處理請(qǐng)求
請(qǐng)求分為:請(qǐng)求頭,請(qǐng)求URL,請(qǐng)求體等
html表單的enctype屬性
在postman的body部分可以查看
- application/x-www-form-urlencode
url方式編碼,較為通用,get和post都可以用 - multipart/form-data
通常適配post方法提交 - text/plain
適合傳遞大量數(shù)據(jù)
ResponseWriter接口涉及方法
- 補(bǔ)充:
fmt.Fprint和fmt.Fprintln能夠?qū)懭隦esponseWriter是因?yàn)镽esponseWriter實(shí)現(xiàn)了io.Writer
接口,fmt.Fprint/Fprintln將數(shù)據(jù)按格式轉(zhuǎn)換為字節(jié)流(如字符串、數(shù)字等),最終調(diào)用io.Writer的Write方法
Writeheader
curl -i localhost:8080/noAuth
或者使用postman進(jìn)行驗(yàn)證
func noAuth(w http.ResponseWriter, r *http.Request) { w.WriteHeader(401) fmt.Fprint(w, "沒有授權(quán),你需要認(rèn)證后訪問") } func main() { http.HandleFunc("/noAuth", noAuth) err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println(err) } }
Header
調(diào)用了Writeheader后的話就不能對(duì)響應(yīng)頭進(jìn)行修改了curl -i http://localhost:8081/redirect
(可以直接看到301)或者postman驗(yàn)證
- 重定向代碼
func Redirect(w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", "http://localhost:8080/hello") // 必須使用包括http的完整的URL! w.WriteHeader(301) } func main() { http.HandleFunc("/redirect", Redirect) if err := http.ListenAndServe(":8081", nil); err != nil { panic(err) } }
- 主服務(wù)代碼
func sayHello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello !!")) } func main() { http.HandleFunc("/hello", sayHello) http.ListenAndServe(":8080", nil) //創(chuàng)建基本服務(wù) }
write
之前都有demo,就是寫入返回,注意需要是[]byte()這樣的表示形式
如果不知道content-type格式可以通過數(shù)據(jù)的前512 比特進(jìn)行確認(rèn)
除了一般的文本字符串之外,還可以返回html和json,下面給出json的示范
type language struct { Language string `json:"language"` //反引號(hào) // 字段名首字母需要大寫才能被 JSON 序列化?。。?! } func uselanguage(w http.ResponseWriter, r *http.Request) { uselanguageis := language{Language: "en"} message, err := json.Marshal(uselanguageis) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") //通過json形式傳遞 w.Write(message) } func main() { http.HandleFunc("/lan", uselanguage) if err := http.ListenAndServe(":8080", nil); err != nil { panic(err) } }
注意一些格式上的細(xì)節(jié),比如字段名首字母需要大寫才能被 JSON 序列化
到此這篇關(guān)于快速上手GO的net/http包的文章就介紹到這了,更多相關(guān)GO net/http包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go+Vue開發(fā)一個(gè)線上外賣應(yīng)用的流程(用戶名密碼和圖形驗(yàn)證碼)
這篇文章主要介紹了Go+Vue開發(fā)一個(gè)線上外賣應(yīng)用(用戶名密碼和圖形驗(yàn)證碼),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11一文帶你掌握Go語言并發(fā)模式中的Context的上下文管理
在?Go?的日常開發(fā)中,Context?上下文對(duì)象無處不在,無論是處理網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫操作還是調(diào)用?RPC?等場(chǎng)景,那你真的熟悉它的正確用法嗎,隨著本文一探究竟吧2023-05-05goland安裝1.7版本報(bào)錯(cuò)Unpacked?SDK?is?corrupted解決
這篇文章主要為大家介紹了goland安裝1.7版本報(bào)錯(cuò)Unpacked?SDK?is?corrupted解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Go語言時(shí)間相關(guān)操作合集(超詳細(xì))
在開發(fā)應(yīng)用程序的過程中,經(jīng)常需要記錄某些操作的時(shí)間或者格式化時(shí)間戳,因此大部分編程語言都會(huì)有操作時(shí)間的庫,Go語言當(dāng)然也不例外,本文我們就一起來學(xué)習(xí)一下time包的使用2023-08-08使用Go語言實(shí)現(xiàn)遠(yuǎn)程傳輸文件
本文主要介紹如何利用Go語言實(shí)現(xiàn)遠(yuǎn)程傳輸文件的功能,有需要的小伙伴們可以參考學(xué)習(xí)。下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)。2016-08-08