48、Go语言基础 - 网络编程 - HTTP Web 编程
hi,我是温新
Web 工作流程
Web 工作流程的简单归纳:
1、客户机通过 TCP/IP 协议建立到服务器的 TCP 连接;
2、客户端向服务器发送 HTTP 协议请求包,请求服务器中的资源;
3、服务器端向客户端发送 HTTP 协议应答包,若请求的资源包含动态语言的内容,则服务器会调用动态语言的解释引擎处理动态内容,并把处理好的内容返回给发起资源请求的客户端;
4、客户端与服务器端断开。
HTTP 协议
HTTP (HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络协议,定义了客户端和服务器端之间请求与响应的传输标准。
Go语言内置的net/http
包十分的优秀,提供了HTTP客户端和服务端的实现。
HTTP 服务端
处理 HTTP 请求
http.ListenAndServe
是 Go 语言标准库中的一个函数,用于启动一个 HTTP 服务器,监听指定的网络地址和端口,然后处理来自客户端的 HTTP 请求。
ListenAndServe
方法格式如下:
func ListenAndServe(addr string, handler http.Handler) error
参数解释:
-
addr
:一个字符串,表示服务器要监听的网络地址和端口。它通常是一个字符串,格式为host:port
,其中host
可以是 IP 地址或主机名,port
是端口号。例如,:8080
表示在本地的 8080 端口上监听,"0.0.0.0:80"
表示在所有网络接口上监听 80 端口。 -
handler
:一个实现了http.Handler
接口的对象,通常是一个处理 HTTP 请求的处理器。处理器是一个函数或对象,用于处理不同路径的请求,可以根据请求路径来调用相应的处理函数。 - 返回值:函数会返回一个
error
,用于指示是否启动服务器时发生错误。如果一切正常,返回nil
。
HandleFunc
http.HandleFunc
是 Go 语言标准库中用于设置 HTTP 请求处理函数的函数。它的主要作用是将指定的处理函数与特定的 URL 路径关联起来,以便在接收到匹配路径的 HTTP 请求时执行该处理函数。
方法格式如下:
func HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
参数解释:
-
pattern
:一个字符串,用于指定要匹配的URL路径模式。可以包括动态部分,例如/user/{id}
,其中{id}
表示一个参数,后续可以通过处理函数访问它。 -
handler
:一个处理函数,它是一个带有两个参数的函数,第一个参数是http.ResponseWriter
,用于编写HTTP响应,第二个参数是*http.Request
,包含HTTP请求的信息。
HTTP Go Web 简单案例
package main
import (
"fmt"
"net/http"
)
func main() {
// 使用 http.HandleFunc 来关联 "/goweb" 路径的 HTTP 请求处理函数
http.HandleFunc("/goweb", func(w http.ResponseWriter, request *http.Request) {
// 在接收到匹配的 HTTP 请求时,执行这个匿名函数
// 使用 fmt.Fprintln 写入 "Go Web" 到 HTTP 响应,发送给客户端
fmt.Fprintln(w, "Go Web")
})
// 启动 HTTP 服务器,监听端口 8888
err := http.ListenAndServe(":8888", nil)
if err != nil {
fmt.Println("HTTP server failed to start: ", err)
}
}
这个示例创建了一个简单的HTTP服务器,当访问http://localhost:8888/goweb
时,它会响应"Go Web"。同时,这演示了使用http.HandleFunc
来创建基本的路由和请求处理。
处理 HTTPS 请求
http.ListenAndServeTLS
是 Go 语言标准库中用于启动支持TLS/SSL的HTTPS服务器的函数。它允许你使用加密传输层安全性(TLS)来保护传输的数据。
ListenAndServeTLS
方法格式如下:
func ListenAndServeTLS(addr, certFile, keyFile string, handler http.Handler) error
参数解释:
-
addr
:一个字符串,表示服务器要监听的网络地址和端口。格式为host:port
,例如:443
表示默认HTTPS端口。 -
certFile
:一个字符串,指定服务器的TLS/SSL证书文件的路径。该证书文件包含公钥和服务器身份信息。 -
keyFile
:一个字符串,指定服务器的TLS/SSL私钥文件的路径。该私钥文件用于解密TLS连接。 -
handler
:一个实现了http.Handler
接口的对象,通常是一个处理HTTP请求的处理器。 - 返回值:函数返回一个
error
,用于指示是否启动服务器时发生错误。如果一切正常,返回nil
ListenAndServeTLS
和 ListenAndServe
和行为是一致的,区别在于只处理 HTTPS 请求。
package main
import (
"fmt"
"net/http"
)
func main() {
// 使用 http.HandleFunc 来设置处理 "/go" 路径的 HTTP 请求处理函数
http.HandleFunc("/go", func(w http.ResponseWriter, r *http.Request) {
// 在接收到匹配的 HTTP 请求时,执行这个匿名函数
// 使用 fmt.Fprintln 将 "Hello, HTTPS World!" 写入 http.ResponseWriter,这将作为 HTTP 响应发送给客户端
fmt.Fprintln(w, "Hello, HTTPS World!")
})
// 启动 HTTPS 服务器,监听默认 HTTPS 端口(443)
// 需要提供 TLS/SSL 证书文件 "server.crt" 和私钥文件 "server.key" 来启用加密连接
err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
if err != nil {
fmt.Println("HTTPS server failed to start: ", err)
}
}
这个示例创建了一个简单的 HTTPS 服务器,当访问 https://localhost/go
时,它会响应 "Hello, HTTPS World!"。需要提供有效的证书和私钥文件,或者使用自签名证书进行测试。
HTTP 客户端
Go 内置的 net/http
包提供了最简洁的 HTTP 客户端实现,不需要借助第三方网络通信库就可以直接使用 HTTP 中用得最多的 GET 和 POST 方式请求数据。
基本的 HTTP/HTTPS 请求
net/http
包的 Client 类型提供了如下几个方法,利用最简洁的方式实现 HTTP 请求:
func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Reqeust) (resp *Response, error)
程序使用完成后 response 必须关闭。
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
// 关闭响应主体
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
GET 请求案例
要请求一个资源,只需调用 http.Get()
方法即可。其格式如下:
func Get(url string) (resp *Response, err error)
参数解释:
-
url
:一个字符串,表示要发送 GET 请求的目标 URL。 - 返回值:
Get
函数返回一个指向http.Response
结构的指针,以及一个error
。如果请求成功,error
为nil
,并且resp
包含了从服务器接收到的响应数据。如果请求失败,error
将包含错误信息。
以下是一个简单的示例代码,使用 http.Get
发起 GET 请求并处理响应:
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
// 要发送 GET 请求的 URL
url := "https://www.ziruchu.com"
// 使用 http.Get 发起 GET 请求
resp, err := http.Get(url)
if err != nil {
fmt.Println("Get reqeuset failed ", err)
return
}
// 确保在函数结束时关闭响应体
defer resp.Body.Close()
// 读取响应体的内容
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Failed to read response body: ", err)
return
}
// 打印响应状态码和响应内容
fmt.Println("Response Status Code:", resp.Status)
fmt.Println("Response Body:", string(body))
}
在这个示例中,http.Get
被用来发送 GET 请求到 https://www.ziruchu.com
,并获取响应。随后,我们读取响应体的内容,并打印响应的状态码和内容。
GET 携带参数的请求案例
package main
import (
"fmt"
"net/http"
)
func main() {
// 处理 HTTP 请求
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
// 解析 URL 参数
r.ParseForm()
queryParams := r.URL.Query()
response := "请求参数:\n"
// 遍历所有参数并构造响应
for key, values := range queryParams {
for _, value := range values {
response += fmt.Sprintf("%s:%s\n", key, value)
}
}
// 将响应写回到浏览器
fmt.Fprintln(w, response)
})
// 启动 HTTP 服务器,监听端口 9999
err := http.ListenAndServe(":9999", nil)
if err != nil {
fmt.Println("HTTP server failed to start:", err)
}
}
在此示例中,设置了一个 HTTP 请求处理函数,当浏览器发起 GET 请求到/user
路径时,它将解析 URL 参数并遍历所有参数内容。然后,它将所有参数的内容构造成响应,并将响应发送回浏览器。
可以在浏览器中访问 http://localhost:9999/user?nam=lucy&age=19&sex=1
,将会看到响应包含所有参数的内容
Post 请求案例
http.Post
是 Go 标准库 net/http
中用于发送 HTTP POST 请求的函数。其格式如下:
func Post(url, contentType string, body io.Reader) (resp *Response, err error)
参数解释:
-
url
:一个字符串,表示要发送 POST 请求的目标 URL。 -
contentType
:一个字符串,表示请求体的数据格式,通常是 MIME 类型,如"application/json"
或"application/x-www-form-urlencoded"
。 -
body
:一个io.Reader
接口,通常是bytes.Reader
或strings.Reader
,包含了要发送的数据。这可以是请求体的内容,如 JSON 数据或表单数据。 - 返回值:
Post
函数返回一个指向http.Response
结构的指针,以及一个error
。如果请求成功,error
为nil
,并且resp
包含了从服务器接收到的响应数据。如果请求失败,error
将包含错误信息。
以下是一个简单的案例:
package main
import (
"bytes"
"fmt"
"net/http"
)
func main() {
// 模拟外部传入的 JSON 数据
jsonData := []byte(`{"name": "王美丽", "age": 19}`)
// 目标 URL
url := "http://192.168.31.90/api"
// 设置请求头中的 Content-Type
contentType := "application/json"
// 使用 http.Post 发起 POST 请求
resp, err := http.Post(url, contentType, bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("POST request failed: ", err)
return
}
defer resp.Body.Close()
// 处理响应
if resp.StatusCode == http.StatusOK {
fmt.Println("POST request was successful!")
// 在这里可以进一步处理服务器的响应数据
} else {
fmt.Println("POST request failed with status code: ", resp.Status)
}
}
在这个示例中,使用 http.Post
发送了一个 JSON 数据,其中 jsonData
包含了参数,url
是目标 URL,contentType
设置为 JSON 数据的 MIME 类型。请替换 url
和 jsonData
为实际的值和数据,以适应你的需求。
PostForm 案例
http.PostForm
是 Go 标准库 net/http
中的一个函数,用于发送 HTTP POST 请求,并以表单形式传递参数。其格式如下:
func PostForm(url string, data url.Values) (resp *Response, err error)
参数解释:
-
url
:一个字符串,表示要发送 POST 请求的目标 URL。 -
data
:一个url.Values
类型的参数,通常用于表示表单数据,其中键和值以键值对的形式存储。 - 返回值:
PostForm
函数返回一个指向http.Response
结构的指针,以及一个error
。如果请求成功,error
为nil
,并且resp
包含了从服务器接收到的响应数据。如果请求失败,error
将包含错误信息。
以下是简单的案例:
package main
import (
"fmt"
"net/http"
"net/url"
)
func main() {
// 要发送 POST 请求的 URL
url := "https://example.com/user"
// 准备表单参数
formData := url.Values{}
formData.Set("name", "王美丽")
formData.Set("age", "19")
// 使用 http.PostForm 发起 POST 请求
resp, err := http.PostForm(url, formData)
if err != nil {
fmt.Println("POST request failed: ", err)
return
}
defer resp.Body.Close() // 确保在函数结束时关闭响应体
// 在这里处理响应...
}