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"}})
程序在使用完回复后必须关闭回复的主体。
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...
func Get(url string) (resp *Response, err error)
Get向指定的URL发出一个GET请求,如果回应的状态码如下,Get会在调用c.CheckRedirect后执行重定向:
如果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))
}
运行结果:
[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
还可以把一些参数做成变量而不是直接放到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))
}
运行结果:
[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
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)
}
运行结果:
[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
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))
}
运行结果:
[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
func Post(url string, bodyType string, body io.Reader) (resp *Response, err error)
Post向指定的URL发出一个POST请求。bodyType为POST数据的类型, body为POST数据,作为请求的主体。如果参数body实现了io.Closer接口,它会在发送请求后被关闭。调用者有责任在读取完返回值resp的主体后关闭它。
func PostForm(url string, data url.Values) (resp *Response, err error)
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))
}
运行结果:
[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
另一种方法:
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))
}
运行结果:
[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
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))
}
运行结果:
[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
要管理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)
// ...
实例演示:
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))
}
运行结果:
[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
Client可以安全的被多个go程同时使用。出于效率考虑,应该一次建立、尽量重用。
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))
实例演示:
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
}
}
将上面的代码编译之后执行,打开浏览器在地址栏输入http://127.0.0.1:9090/hello回车,此时就能够看到 Hello world!
要管理服务端的行为,可以创建一个自定义的Server:
s := &http.Server{
Addr: ":8080",
Handler: myHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())