• Go Web——http标准库



    简介

    Golang http标准库提供了HTTP客户端和服务端的实现。

    注意了,客户端实现可以发出http请求,并解析响应。服务器可以实现http server功能。市面上的所有golang web框架都是基于http标准库实现的。

    Get、Head、Post和PostForm函数发出HTTP/ HTTPS请求。

    resp, err := http.Get("http://example.com/")
    ...
    resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
    ...
    resp, err := http.PostForm("http://example.com/form",
    	url.Values{"key": {"Value"}, "id": {"123"}})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    程序在使用完回复后必须关闭回复的主体。

    resp, err := http.Get("http://example.com/")
    if err != nil {
    	// handle error
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    // ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    一 HTTP Client

    1.1 发送GET请求

    func Get(url string) (resp *Response, err error)
    
    • 1

    Get向指定的URL发出一个GET请求,如果回应的状态码如下,Get会在调用c.CheckRedirect后执行重定向:

    • 301 (Moved Permanently)
    • 302 (Found)
    • 303 (See Other)
    • 307 (Temporary Redirect)

    如果c.CheckRedirect执行失败或存在HTTP协议错误时,本方法将返回该错误;如果回应的状态码不是2xx,本方法并不会返回错误。如果返回值err为nil,resp.Body总是非nil的,调用者应该在读取完resp.Body后关闭它

    Get是对包变量DefaultClient的Get方法的包装。

    实例演示:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"log"
    	"net/http"
    )
    
    func main() {
    	url := "http://apis.juhe.cn/simpleWeather/query?city=成都&key=904d15a2893c4d81c5c5a1fd0a7a7ed6"
    	r, err := http.Get(url)
    	if err != nil {
    		log.Fatal(err)
    	}
    	b, err := ioutil.ReadAll(r.Body)
    	r.Body.Close()	// 程序在使用完response后必须关闭回复的主体。
    	if err != nil {
    		log.Fatal(err)
    	}
    	fmt.Printf("b: %v\n", string(b))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    b: {"reason":"查询成功!","result":{"city":"成都","realtime":{"temperature":"39","humidity":"30","info":"多云","wid":"01","direct":"西南风","power":"3级","aqi":"78"},"future":[{"date":"2022-08-21","temperature":"27\/39℃","weather":"多云转阴","wid":{"day":"01","night":"02"},"direct":"持续无风向"},{"date":"2022-08-22","temperature":"27\/39℃","weather":"阴转晴","wid":{"day":"02","night":"00"},"direct":"持续无风向"},{"date":"2022-08-23","temperature":"29\/38℃","weather":"多云","wid":{"day":"01","night":"01"},"direct":"持续无风向"},{"date":"2022-08-24","temperature":"27\/37℃","weather":"多云转小雨","wid":{"day":"01","night":"07"},"direct":"持续无风向"},{"date":"2022-08-25","temperature":"23\/35℃","weather":"小雨转中雨","wid":{"day":"07","night":"08"},"direct":"持续无风向"}]},"error_code":0}
    
    [Done] exited with code=0 in 1.636 seconds
    
    • 1
    • 2
    • 3
    • 4

    还可以把一些参数做成变量而不是直接放到URL里:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"log"
    	"net/http"
    	"net/url"
    )
    
    func main() {
    	params := url.Values{}
    	u, err := url.Parse("http://apis.juhe.cn/simpleWeather/query")
    	if err != nil {
    		log.Fatal(err)
    	}
    	params.Set("city", "成都")
    	params.Set("key", "904d15a2893c4d81c5c5a1fd0a7a7ed6")
    
    	// 如果参数中有中文,则堆params进行encode
    	u.RawQuery = params.Encode()
    	urlPath := u.String()
    	fmt.Printf("urlPath: %v\n", urlPath)
    
    	r, err := http.Get(urlPath)
    	if err != nil {
    		log.Fatal(err)
    	}
    	b, err := ioutil.ReadAll(r.Body)
    	r.Body.Close()
    	if err != nil {
    		log.Fatal(err)
    	}
    	fmt.Printf("b: %v\n", string(b))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    urlPath: http://apis.juhe.cn/simpleWeather/query?city=%E6%88%90%E9%83%BD&key=904d15a2893c4d81c5c5a1fd0a7a7ed6
    b: {"reason":"查询成功!","result":{"city":"成都","realtime":{"temperature":"39","humidity":"34","info":"多云","wid":"01","direct":"西南风","power":"3级","aqi":"78"},"future":[{"date":"2022-08-21","temperature":"27\/39℃","weather":"多云转阴","wid":{"day":"01","night":"02"},"direct":"持续无风向"},{"date":"2022-08-22","temperature":"27\/39℃","weather":"阴转晴","wid":{"day":"02","night":"00"},"direct":"持续无风向"},{"date":"2022-08-23","temperature":"29\/38℃","weather":"多云","wid":{"day":"01","night":"01"},"direct":"持续无风向"},{"date":"2022-08-24","temperature":"27\/37℃","weather":"多云转小雨","wid":{"day":"01","night":"07"},"direct":"持续无风向"},{"date":"2022-08-25","temperature":"23\/35℃","weather":"小雨转中雨","wid":{"day":"07","night":"08"},"direct":"持续无风向"}]},"error_code":0}
    
    [Done] exited with code=0 in 2.198 seconds
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.2 解析JSON类型返回结果

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"io/ioutil"
    	"log"
    	"net/http"
    )
    
    func main() {
    	type result struct {
    		Args    string            `json:"args"`
    		Headers map[string]string `json:"headers"`
    		Origin  string            `json:"origin"`
    		Url     string            `json:"url"`
    	}
    
    	r, err := http.Get("http://httpbin.org/get")
    	if err != nil {
    		log.Fatal(err)
    	}
    	b, err := ioutil.ReadAll(r.Body)
    	defer r.Body.Close()
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	fmt.Printf("b: %v\n", string(b))
    
    	var res result
    	_ = json.Unmarshal(b, &res)
    	fmt.Printf("res: %#v\n", res)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    b: {
      "args": {}, 
      "headers": {
        "Accept-Encoding": "gzip", 
        "Host": "httpbin.org", 
        "User-Agent": "Go-http-client/1.1", 
        "X-Amzn-Trace-Id": "Root=1-6301e9e7-6a3a381f2ae14307082b524b"
      }, 
      "origin": "171.223.97.152", 
      "url": "http://httpbin.org/get"
    }
    
    res: main.result{Args:"", Headers:map[string]string{"Accept-Encoding":"gzip", "Host":"httpbin.org", "User-Agent":"Go-http-client/1.1", "X-Amzn-Trace-Id":"Root=1-6301e9e7-6a3a381f2ae14307082b524b"}, Origin:"171.223.97.152", Url:"http://httpbin.org/get"}
    
    [Done] exited with code=0 in 2.287 seconds
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.3 GET请求添加请求头

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"log"
    	"net/http"
    )
    
    func main() {
    	client := &http.Client{}
    	request, err := http.NewRequest("GET", "http://httpbin.org/get", nil)
    	if err != nil {
    		log.Fatal(err)
    	}
    	request.Header.Add("name", "Psych")
    	request.Header.Add("age", "18")
    
    	response, err := client.Do(request)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	body, err := ioutil.ReadAll(response.Body)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	fmt.Printf("body: %v\n", string(body))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    body: {
      "args": {}, 
      "headers": {
        "Accept-Encoding": "gzip", 
        "Age": "18", 
        "Host": "httpbin.org", 
        "Name": "Psych", 
        "User-Agent": "Go-http-client/1.1", 
        "X-Amzn-Trace-Id": "Root=1-6301eb7f-0d585b7d5877f5f51ea2cc21"
      }, 
      "origin": "171.223.97.152", 
      "url": "http://httpbin.org/get"
    }
    
    
    [Done] exited with code=0 in 2.818 seconds
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.4 发送POST请求

    func Post(url string, bodyType string, body io.Reader) (resp *Response, err error)
    
    • 1

    Post向指定的URL发出一个POST请求。bodyType为POST数据的类型, body为POST数据,作为请求的主体。如果参数body实现了io.Closer接口,它会在发送请求后被关闭。调用者有责任在读取完返回值resp的主体后关闭它。

    func PostForm(url string, data url.Values) (resp *Response, err error)
    
    • 1

    PostForm向指定的URL发出一个POST请求,url.Values类型的data会被编码为请求的主体。如果返回值err为nil,resp.Body总是非nil的,调用者应该在读取完resp.Body后关闭它。

    实例演示:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"log"
    	"net/http"
    	"net/url"
    )
    
    func main() {
    	path := "http://apis.juhe.cn/simpleWeather/query"
    	urlValues := url.Values{}
    	urlValues.Add("city", "成都")
    	urlValues.Add("key", "904d15a2893c4d81c5c5a1fd0a7a7ed6")
    
    	r, err := http.PostForm(path, urlValues)
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer r.Body.Close()
    
    	b, err := ioutil.ReadAll(r.Body)
    	if err != nil {
    		log.Fatal(err)
    	}
    	fmt.Printf("天气信息: %v\n", string(b))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    body: {
      "args": {}, 
      "headers": {
        "Accept-Encoding": "gzip", 
        "Age": "18", 
        "Host": "httpbin.org", 
        "Name": "Psych", 
        "User-Agent": "Go-http-client/1.1", 
        "X-Amzn-Trace-Id": "Root=1-6301eb7f-0d585b7d5877f5f51ea2cc21"
      }, 
      "origin": "171.223.97.152", 
      "url": "http://httpbin.org/get"
    }
    
    
    [Done] exited with code=0 in 2.818 seconds
    
    [Running] go run "e:\golang开发学习\go_pro\test.go"
    天气信息: {"reason":"查询成功!","result":{"city":"成都","realtime":{"temperature":"39","humidity":"30","info":"多云","wid":"01","direct":"南风","power":"3级","aqi":"84"},"future":[{"date":"2022-08-21","temperature":"27\/39℃","weather":"多云转阴","wid":{"day":"01","night":"02"},"direct":"持续无风向"},{"date":"2022-08-22","temperature":"27\/39℃","weather":"阴转晴","wid":{"day":"02","night":"00"},"direct":"持续无风向"},{"date":"2022-08-23","temperature":"29\/38℃","weather":"多云","wid":{"day":"01","night":"01"},"direct":"持续无风向"},{"date":"2022-08-24","temperature":"27\/37℃","weather":"多云转小雨","wid":{"day":"01","night":"07"},"direct":"持续无风向"},{"date":"2022-08-25","temperature":"23\/35℃","weather":"小雨转中雨","wid":{"day":"07","night":"08"},"direct":"持续无风向"}]},"error_code":0}
    
    [Done] exited with code=0 in 1.666 seconds
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    另一种方法:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"net/url"
    	"strings"
    )
    
    func main() {
    	urlValues := url.Values{
    		"name": {"Psych"},
    		"age":  {"18"},
    	}
    	requestBody := urlValues.Encode()
    	response, _ := http.Post("http://httpbin.org/post", "text/html", strings.NewReader(requestBody))
    	body, _ := ioutil.ReadAll(response.Body)
    	fmt.Printf("信息: %v\n", string(body))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    信息: {
      "args": {}, 
      "data": "age=18&name=Psych", 
      "files": {}, 
      "form": {}, 
      "headers": {
        "Accept-Encoding": "gzip", 
        "Content-Length": "17", 
        "Content-Type": "text/html", 
        "Host": "httpbin.org", 
        "User-Agent": "Go-http-client/1.1", 
        "X-Amzn-Trace-Id": "Root=1-6301f289-16bb6b7364feb63441b6a3c2"
      }, 
      "json": null, 
      "origin": "171.223.97.152", 
      "url": "http://httpbin.org/post"
    }
    
    
    [Done] exited with code=0 in 2.616 seconds
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.5 发送JSON数据的POST请求

    package main
    
    import (
    	"bytes"
    	"encoding/json"
    	"fmt"
    	"io/ioutil"
    	"net/http"
    )
    
    func main() {
    	data := make(map[string]interface{})
    	data["site"] = "www.baidu.com"
    	data["name"] = "百度"
    
    	bytesData, _ := json.Marshal(data)
    	response, _ := http.Post("http://httpbin.org/post", "application/json", bytes.NewReader(bytesData))
    
    	body, _ := ioutil.ReadAll(response.Body)
    	fmt.Printf("信息: %v\n", string(body))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    信息: {
      "args": {}, 
      "data": "{\"name\":\"\u767e\u5ea6\",\"site\":\"www.baidu.com\"}", 
      "files": {}, 
      "form": {}, 
      "headers": {
        "Accept-Encoding": "gzip", 
        "Content-Length": "40", 
        "Content-Type": "application/json", 
        "Host": "httpbin.org", 
        "User-Agent": "Go-http-client/1.1", 
        "X-Amzn-Trace-Id": "Root=1-6301f466-03c547cb3c1424ae5e9439ca"
      }, 
      "json": {
        "name": "\u767e\u5ea6", 
        "site": "www.baidu.com"
      }, 
      "origin": "171.223.97.152", 
      "url": "http://httpbin.org/post"
    }
    
    
    [Done] exited with code=0 in 2.056 seconds
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1.6 使用Client自定义请求

    要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client:

    client := &http.Client{
    	CheckRedirect: redirectPolicyFunc,
    }
    resp, err := client.Get("http://example.com")
    // ...
    req, err := http.NewRequest("GET", "http://example.com", nil)
    // ...
    req.Header.Add("If-None-Match", `W/"wyzzy"`)
    resp, err := client.Do(req)
    // ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    实例演示:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"log"
    	"net/http"
    	"time"
    )
    
    func main() {
    	client := http.Client{
    		Timeout: time.Second * 5,
    	}
    	url := "http://apis.juhe.cn/simpleWeather/query?city=成都&key=904d15a2893c4d81c5c5a1fd0a7a7ed6"
    	request, err := http.NewRequest(http.MethodGet, url, nil)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	request.Header.Add("referer", "http://apis.juhe.cn/")
    	res, err := client.Do(request)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	defer res.Body.Close()
    
    	b, _ := ioutil.ReadAll(res.Body)
    	fmt.Printf("信息: %v\n", string(b))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行结果:

    [Running] go run "e:\golang开发学习\go_pro\test.go"
    信息: {"reason":"查询成功!","result":{"city":"成都","realtime":{"temperature":"39","humidity":"31","info":"多云","wid":"01","direct":"南风","power":"3级","aqi":"83"},"future":[{"date":"2022-08-21","temperature":"27\/39℃","weather":"多云转阴","wid":{"day":"01","night":"02"},"direct":"持续无风向"},{"date":"2022-08-22","temperature":"27\/39℃","weather":"阴转晴","wid":{"day":"02","night":"00"},"direct":"持续无风向"},{"date":"2022-08-23","temperature":"29\/38℃","weather":"多云","wid":{"day":"01","night":"01"},"direct":"持续无风向"},{"date":"2022-08-24","temperature":"27\/37℃","weather":"多云转小雨","wid":{"day":"01","night":"07"},"direct":"持续无风向"},{"date":"2022-08-25","temperature":"23\/35℃","weather":"小雨转中雨","wid":{"day":"07","night":"08"},"direct":"持续无风向"}]},"error_code":0}
    
    [Done] exited with code=0 in 1.846 seconds
    
    • 1
    • 2
    • 3
    • 4

    Client可以安全的被多个go程同时使用。出于效率考虑,应该一次建立、尽量重用。

    二 HTTP Server

    2.1 默认的Server

    ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。

    Handle和HandleFunc函数可以向DefaultServeMux添加处理器。

    http.Handle("/foo", fooHandler)
    http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    实例演示:

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    // http server
    // 请求处理函数
    func sayHello(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintln(w, "Hello world!")
    }
    
    func main() {
    	// 响应路径,注意前面要有斜杠/
    	http.HandleFunc("/hello", sayHello)
    
    	// 设置监听窗口并监听,注意前面要有冒号:
    	err := http.ListenAndServe(":9090", nil)
    	if err != nil {
    		fmt.Printf("http server failed, err:%v\n", err)
    		return
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    将上面的代码编译之后执行,打开浏览器在地址栏输入http://127.0.0.1:9090/hello回车,此时就能够看到 Hello world!

    2.2 自定义Server

    要管理服务端的行为,可以创建一个自定义的Server:

    s := &http.Server{
    	Addr:           ":8080",
    	Handler:        myHandler,
    	ReadTimeout:    10 * time.Second,
    	WriteTimeout:   10 * time.Second,
    	MaxHeaderBytes: 1 << 20,
    }
    log.Fatal(s.ListenAndServe())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    java-php-python-ssm一起组局校园交友平台计算机毕业设计
    Cobbler
    ChatGpt 反向代理
    九. Linux网络命令
    浅谈java单元测试框架junit4/5
    阿里云——OpenAPI使用——短信服务
    Flask基础学习笔记
    尚硅谷Webpack实战教程基础配置
    分治法、动态规划、贪心算法区别
    对象、数组深拷贝与浅拷贝
  • 原文地址:https://blog.csdn.net/qq_39280718/article/details/126468998