快速上手GO的net/http包
針對GO中net/http包的學習筆記
基礎快速了解
創(chuàng)建簡單的GOHTTP服務
func main() { http.HandleFunc("/hello", sayHello) http.ListenAndServe(":8080", nil) //創(chuàng)建基本服務 } func sayHello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, World!")) }
訪問8080/hello進行測試
Handler接口定義:(這部分后面又詳細解釋)
type Handler interface { ServeHTTP(ResponseWriter, *Request) } //只要有ServeHTTP方法就行
可以自己實現(xiàn)這個接口
同時http提供了handlerFunc結構體
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } //本質(zhì)上就是調(diào)用自身,因為也是一個函數(shù),不過serveHTTP的內(nèi)容自己可以定義改動
和之前的HandleFunc區(qū)分,HandleFunc是用來給不同路徑綁定方法的,少一個r
那么只要滿足Handlerfunc 的簽名形式,就可以進行類型轉換
- 類型轉換的正常的理解例子
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進行轉換 res := ans.add(1, 2) fmt.Println(res) //3 }
http.HandleFunc("/hello", hello)
這個后面的簽名只要是 func(ResponseWriter, *Request)
就可以了
但是
http.ListenAndServe(":8080", referer)
這個后面的函數(shù)需要是滿足Handler接口,有serveHTTP方法
嘗試搭建檢測是在query中有name = red
即http://localhost:8080/hello?name=red
發(fā)現(xiàn)會有重復覆蓋路由的問題,因為listenandServe會攔截所有的路由,后面再解決
type CheckQueryName struct { wantname string handler http.Handler } func (this *CheckQueryName) ServeHTTP(w http.ResponseWriter, r *http.Request) { queryParams := r.URL.Query() //獲取get請求的query name := queryParams.Get("name") if name == "red" { this.handler.ServeHTTP(w, r) //其實就是調(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{ //用&因為serveHTTP方法定義在指針接收器上 wantname: "red", handler: http.HandlerFunc(checkforname), } http.HandleFunc("/hello", hello) //滿足func(ResponseWriter, *Request)簽名就可以 http.ListenAndServe(":8080", thecheck) //直接監(jiān)視8080的所有端口,攔截所有路由 }
編寫簡單的GET請求客戶端
利用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) //等待獲取請求 }
但是如果把網(wǎng)址換成baidu.com就會獲取不到,這是因為轉發(fā),以及沒有User-agent的問題
編寫自定義的GET請求客戶端
利用http.Client可以進行自定義
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) } // 添加請求頭 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請求的整個周期包括請求準備,建立連接,發(fā)送請求,請求重定向,接收響應等等 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信息
編寫默認的post請求客戶端
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請求失敗: %v\n", err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Printf("POST響應: %s\n", string(body)) }
string.NewReader是一種方法將格式改為io reader可以讀取的形式,接收的話也可以postData.Read讀取。
這一類的方式比較多,不一一匯總
對應的server.go (需要在終端中go run server.go )兩個終端分別運行服務端,客戶端
func main() { // 處理 /users 路徑的 POST 請求 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 } // 讀取請求體 body, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "讀取請求失敗: %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 } // 設置響應頭 w.Header().Set("Content-Type", "application/json") // 構造響應數(shù)據(jù) response := map[string]interface{}{ "message": "success", "data": map[string]interface{}{ "name": user.Name, "age": user.Age, }, } // 返回 JSON 響應 json.NewEncoder(w).Encode(response) //將 response 對象轉換為 JSON 格式并寫入響應 //等價于: // jsonData, err := json.Marshal(response) // if err != nil { // w.WriteHeader(http.StatusInternalServerError) // return // } // w.Write(jsonData) }) // 啟動服務器 fmt.Println("服務器啟動在 :8080 端口...") if err := http.ListenAndServe(":8080", nil); err != nil { panic(err) } }
多路復用器
DefaultServeMux一般不會使用,因為會有沖突等等問題,所以一般用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{}) //多引入結構體,后面會知道有好處 // 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)建多個路由器實例,用于不同的目的。同時可以為不同的路由器配置不同的中間件(用著先)
第三方有庫httprouter,比如可以解決url不能是變量代表的問題,了解就行
更多都是使用restful API進行開發(fā)的~目前的了解有個概念就行
處理器函數(shù)
Handle
注冊處理器過程中調(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"}) //會調(diào)用對應的serveHTTP方法 mux.Handle("/lily", &username{name: "lily"}) //可以為不同的路徑使用相同的處理器結構體,但傳入不同的參數(shù) //這就是比用handleFunc()更靈活的地方 server := &http.Server{ Addr: ":8080", Handler: mux, } if err := server.ListenAndServe(); err != nil { //防止錯誤 panic(err) } }
HandlleFunc
處理器函數(shù):HandleFunc
(注意不是HandlerFunc,沒有r !! HandleFunc 是處理器函數(shù))
之前已經(jīng)學習過,定義就是func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
深入源代碼會發(fā)現(xiàn)內(nèi)部也是借助serveMux對象,從而實現(xiàn)了Handler的ServeHTTP()方法的
Handler
Handler就是處理器接口,實現(xiàn)ServeHTTP方法的,之前展示過
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
HandlerFunc
HandlerFunc是結構體,用于實現(xiàn)接口的
定義:
type HandlerFunc func(ResponseWriter, *Request)
用于連接處理器Handle和處理器函數(shù)HandleFunc,它實現(xiàn)了 Handler 接口,使得函數(shù)可以直接當作處理器使用:
- 理解“連接連接處理器Handle和處理器函數(shù)HandleFunc”:(之前也學過)
// 方式一:普通函數(shù) func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello!") } // 注冊方式一:將函數(shù)轉換為 HandlerFunc http.Handle("/hello", http.HandlerFunc(hello)) // 方式二:直接使用 HandleFunc http.HandleFunc("/hello", hello)
處理請求
請求分為:請求頭,請求URL,請求體等
html表單的enctype屬性
在postman的body部分可以查看
- application/x-www-form-urlencode
url方式編碼,較為通用,get和post都可以用 - multipart/form-data
通常適配post方法提交 - text/plain
適合傳遞大量數(shù)據(jù)
ResponseWriter接口涉及方法
- 補充:
fmt.Fprint和fmt.Fprintln能夠?qū)懭隦esponseWriter是因為ResponseWriter實現(xiàn)了io.Writer
接口,fmt.Fprint/Fprintln將數(shù)據(jù)按格式轉換為字節(jié)流(如字符串、數(shù)字等),最終調(diào)用io.Writer的Write方法
Writeheader
curl -i localhost:8080/noAuth
或者使用postman進行驗證
func noAuth(w http.ResponseWriter, r *http.Request) { w.WriteHeader(401) fmt.Fprint(w, "沒有授權,你需要認證后訪問") } func main() { http.HandleFunc("/noAuth", noAuth) err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println(err) } }
Header
調(diào)用了Writeheader后的話就不能對響應頭進行修改了curl -i http://localhost:8081/redirect
(可以直接看到301)或者postman驗證
- 重定向代碼
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) } }
- 主服務代碼
func sayHello(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello !!")) } func main() { http.HandleFunc("/hello", sayHello) http.ListenAndServe(":8080", nil) //創(chuàng)建基本服務 }
write
之前都有demo,就是寫入返回,注意需要是[]byte()這樣的表示形式
如果不知道content-type格式可以通過數(shù)據(jù)的前512 比特進行確認
除了一般的文本字符串之外,還可以返回html和json,下面給出json的示范
type language struct { Language string `json:"language"` //反引號 // 字段名首字母需要大寫才能被 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) } }
注意一些格式上的細節(jié),比如字段名首字母需要大寫才能被 JSON 序列化
到此這篇關于快速上手GO的net/http包的文章就介紹到這了,更多相關GO net/http包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang基于errgroup實現(xiàn)并發(fā)調(diào)用的方法
這篇文章主要介紹了golang基于errgroup實現(xiàn)并發(fā)調(diào)用,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09Go語言時間管理利器之深入解析time模塊的實戰(zhàn)技巧
本文深入解析了Go語言標準庫中的time模塊,揭示了其高效用法和實用技巧,通過學習time模塊的三大核心類型(Time、Duration、Timer/Ticker)以及高頻使用場景,開發(fā)者可以更好地處理時間相關的任務,感興趣的朋友一起看看吧2025-03-03Go語言實現(xiàn)互斥鎖、隨機數(shù)、time、List
這篇文章主要介紹了Go語言實現(xiàn)互斥鎖、隨機數(shù)、time、List的相關資料,需要的朋友可以參考下2018-10-10golang常用庫之字段參數(shù)驗證庫-validator使用詳解
這篇文章主要介紹了golang常用庫:字段參數(shù)驗證庫-validator使用,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借價值,需要的朋友可以參考下2020-10-10golang?struct?json?tag的使用以及深入講解
這篇文章主要給大家介紹了關于golang?struct?json?tag的使用以及深入講解,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-02-02