快速上手GO的net/http包
針對GO中net/http包的學習筆記
基礎(chǔ)快速了解
創(chuàng)建簡單的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進行測試
Handler接口定義:(這部分后面又詳細解釋)
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
//只要有ServeHTTP方法就行
可以自己實現(xiàn)這個接口
同時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)用自身,因為也是一個函數(shù),不過serveHTTP的內(nèi)容自己可以定義改動
和之前的HandleFunc區(qū)分,HandleFunc是用來給不同路徑綁定方法的,少一個r
那么只要滿足Handlerfunc 的簽名形式,就可以進行類型轉(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進行轉(zhuǎn)換
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)會有重復(fù)覆蓋路由的問題,因為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就會獲取不到,這是因為轉(zhuǎn)發(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ā)送請求,請求重定向,接收響應(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信息
編寫默認的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響應(yīng): %s\n", string(body))
}
string.NewReader是一種方法將格式改為io reader可以讀取的形式,接收的話也可以postData.Read讀取。
這一類的方式比較多,不一一匯總
對應(yīng)的server.go (需要在終端中g(shù)o run server.go )兩個終端分別運行服務(wù)端,客戶端
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
}
// 設(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 對象轉(zhuǎn)換為 JSON 格式并寫入響應(yīng)
//等價于:
// jsonData, err := json.Marshal(response)
// if err != nil {
// w.WriteHeader(http.StatusInternalServerError)
// return
// }
// w.Write(jsonData)
})
// 啟動服務(wù)器
fmt.Println("服務(wù)器啟動在 :8080 端口...")
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
多路復(fù)用器
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{}) //多引入結(jié)構(gòu)體,后面會知道有好處
// 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)用對應(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 { //防止錯誤
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是結(jié)構(gòu)體,用于實現(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ù)轉(zhuǎn)換為 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ù)按格式轉(zhuǎn)換為字節(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, "沒有授權(quán),你需要認證后訪問")
}
func main() {
http.HandleFunc("/noAuth", noAuth)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println(err)
}
}
Header
調(diào)用了Writeheader后的話就不能對響應(yīng)頭進行修改了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)
}
}
- 主服務(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 比特進行確認
除了一般的文本字符串之外,還可以返回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 序列化
到此這篇關(guān)于快速上手GO的net/http包的文章就介紹到這了,更多相關(guān)GO net/http包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go+Vue開發(fā)一個線上外賣應(yīng)用的流程(用戶名密碼和圖形驗證碼)
這篇文章主要介紹了Go+Vue開發(fā)一個線上外賣應(yīng)用(用戶名密碼和圖形驗證碼),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
一文帶你掌握Go語言并發(fā)模式中的Context的上下文管理
在?Go?的日常開發(fā)中,Context?上下文對象無處不在,無論是處理網(wǎng)絡(luò)請求、數(shù)據(jù)庫操作還是調(diào)用?RPC?等場景,那你真的熟悉它的正確用法嗎,隨著本文一探究竟吧2023-05-05
goland安裝1.7版本報錯Unpacked?SDK?is?corrupted解決
這篇文章主要為大家介紹了goland安裝1.7版本報錯Unpacked?SDK?is?corrupted解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11

