GO:HTTP客户端和服务端demo
客户端:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func main() {
/* 示例1:GET */
{
fmt.Println("+++++++++++++++++++++++++++++++++++++++++++++")
/* 发请求收应答 */
ack, err := http.Get("http://127.0.0.1:1280/")
if err != nil {
panic(err)
}
/* 读取应答正文 */
ackBody, err := ioutil.ReadAll(ack.Body)
/* 关闭应答正文,释放资源,无论是否异常 */
ack.Body.Close()
if err != nil {
panic(err)
}
/* 输出应答状态 */
fmt.Printf("HTTP Response StatusCode: %d\n", ack.StatusCode)
fmt.Printf("HTTP Response Status: %s\n", ack.Status)
/* 输出应答头域 */
fmt.Printf("HTTP Response HEADER: %s\n", ack.Header.Get("my-http-head"))
/* 输出应答正文 */
fmt.Printf("HTTP Response BODY: %s\n", ackBody)
}
/* 示例2:POST */
{
fmt.Println("---------------------------------------------")
/* 构建请求正文 */
reqBody := strings.NewReader(`
{
"name": "test1280"
}
`)
/* 创建请求对象 */
req, err := http.NewRequest("POST", "http://127.0.0.1:1281/", reqBody)
if err != nil {
panic(err)
}
/* 设置请求头域 */
req.Header.Set("Content-Type", "application/json")
/* 发请求收应答 */
ack, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
/* 读取应答正文 */
ackBody, err := ioutil.ReadAll(ack.Body)
/* 关闭应答正文,释放资源,无论是否异常 */
ack.Body.Close()
if err != nil {
panic(err)
}
/* 输出应答状态 */
fmt.Printf("HTTP Response StatusCode: %d\n", ack.StatusCode)
fmt.Printf("HTTP Response Status: %s\n", ack.Status)
/* 输出应答头域 */
fmt.Printf("HTTP Response HEADER: %s\n", ack.Header.Get("my-http-head"))
/* 输出应答正文 */
fmt.Printf("HTTP Response BODY: %s\n", ackBody)
}
}
服务端:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
/* 监听一个端口方式 */
go func() {
/* 使用http.DefaultServeMux */
http.HandleFunc("/", handle)
http.ListenAndServe(":1280", nil)
}()
/* 监听多个端口方式 */
go func() {
/* 自定义ServeMux */
mux := http.NewServeMux()
mux.HandleFunc("/", handle)
http.ListenAndServe(":1281", mux)
}()
/* 测试代码,保证主线程/协程不退出 */
time.Sleep(time.Second*60*10)
}
func handle(w http.ResponseWriter, r *http.Request) {
/* 读取发请求客户端地址 */
fmt.Printf("Receive HTTP REQ FROM: %s\n", r.RemoteAddr)
/* 读取请求方法 */
fmt.Printf("Method: %s\n", r.Method)
/* 读取请求URL */
fmt.Printf("URL: %s\n", r.URL)
/* 读取请求头域 */
// key and values
for k, vs := range r.Header {
for _, v := range vs {
fmt.Printf("HTTP HEADER: %s\t\t:%s\n", k, v)
}
}
/* 读取请求正文 */
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
/* 输出请求正文 */
fmt.Printf("HTTP REQ BODY:\n%s\n", reqBody)
/* 不需要显式关闭http.Request.Body */
/* 设置HTTP应答头 */
w.Header().Set("my-http-head", fmt.Sprintf("HTTP-REQ-METHOD-%s", r.Method))
/* 生成应答正文 */
ackBody, err := json.Marshal(r.Header)
if err != nil {
panic(err)
}
/* 设置应答状态 */
w.WriteHeader(201)
/* 设置应答正文 */
w.Write(ackBody)
}
运行服务端:
test1280>go run http_server.go
Receive HTTP REQ FROM: 127.0.0.1:55078
Method: GET
URL: /
HTTP HEADER: User-Agent :Go-http-client/1.1
HTTP HEADER: Accept-Encoding :gzip
HTTP REQ BODY:
Receive HTTP REQ FROM: 127.0.0.1:35026
Method: POST
URL: /
HTTP HEADER: Content-Length :36
HTTP HEADER: Content-Type :application/json
HTTP HEADER: Accept-Encoding :gzip
HTTP HEADER: User-Agent :Go-http-client/1.1
HTTP REQ BODY:
{
"name": "test1280"
}
^Csignal: interrupt
运行客户端:
test1280>go run http_client.go
+++++++++++++++++++++++++++++++++++++++++++++
HTTP Response StatusCode: 201
HTTP Response Status: 201 Created
HTTP Response HEADER: HTTP-REQ-METHOD-GET
HTTP Response BODY: {"Accept-Encoding":["gzip"],"User-Agent":["Go-http-client/1.1"]}
---------------------------------------------
HTTP Response StatusCode: 201
HTTP Response Status: 201 Created
HTTP Response HEADER: HTTP-REQ-METHOD-POST
HTTP Response BODY: {"Accept-Encoding":["gzip"],"Content-Length":["36"],"Content-Type":["application/json"],"User-Agent":["Go-http-client/1.1"]}
go官网给的样例很不错:
https://golang.org/pkg/net/http/#example_Get
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
res, err := http.Get("http://www.google.com/robots.txt")
if err != nil {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", robots)
}
以前我常会写为:
robots, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
但是这样写存在问题:
1.如果不用“某个资源”,就应该尽可能快地释放它。
res.Body,就是一个“资源”,会占用内存等有限的系统资源。
如果确保后续代码中不使用它,那么不要使用defer延迟调用释放,而应该立刻调用释放其占用的资源。
2.错误时也应当关闭。
ioutil.ReadAll和res.Body是否关闭没有啥关系。
不应该在ioutil.ReadAll错误时,放任res.Body不关闭、不释放它。
读取完之后,立刻关闭释放,无论是否读取异常。
参考:
1.https://golang.org/pkg/net/http/#example_Get
2.https://ganlvtech.github.io/2018/12/08/go-http-server-demo/