✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑
文章目录
- Go语言http编程
- 一、HTTP基础概念
- 1. HTTP请求
- 2. HTTP响应
- 3. HTTP/1.1与HTTP/2
- 二、http编程实战
- 1. HTTP服务器
- 2. HTTP客户端
- 3. 带参数的请求
- 4. POST请求提交Form表单
- 5. POST请求提交Json数据
- 三、错误处理
Go语言http编程
HTTP(HyperText Transfer Protocol)是互联网上应用最广泛的一种网络协议。无论是浏览网页、使用API进行数据传输,还是实现微服务架构,HTTP都扮演着至关重要的角色。Go语言(又称Golang)以其简洁、高效和强大的并发处理能力,成为了HTTP编程的优选语言之一。
一、HTTP基础概念
在深入了解Go语言的HTTP编程之前,我们需要先了解一些HTTP的基础知识。
1. HTTP请求
请求方法(GET、POST、PUT、DELETE等)
请求URL(包括协议、主机名、端口号、路径和查询参数)
请求头(包含元数据,如Content-Type、Authorization等)
请求体(对于POST、PUT等请求方法,通常包含要发送的数据)
2. HTTP响应
状态码(如200 OK、404 Not Found、500 Internal Server Error等)
响应头(包含元数据,如Content-Type、Location等)
响应体(包含返回的数据)
3. HTTP/1.1与HTTP/2
HTTP/1.1是广泛使用的版本,支持持久连接和缓存控制。
HTTP/2引入了多路复用、头部压缩和服务器推送等特性,提高了性能。
二、http编程实战
1. HTTP服务器
Go语言内置的net/http包提供了简单的HTTP服务器实现。
开启监听程序的代码是放在main方法的最后一行的
以下是一个基本的HTTP服务器示例:
// http_server.go
package main
import (
"fmt"
"net/http"
)
// 定义发送接收数据函数
func helloHandler(w http.ResponseWriter, r *http.Request) {
// 给浏览器响应数据
// 一般会响应一些信息给客户端 (文字、网页) resp.Write
// 响应一段文字[]byte("hello,web")
// 响应一段html代码 []byte("html代码") 网页
// Write([]byte) (int, error)
_, err := w.Write([]byte("<h1>感谢大家来到景天科技苑!</h1>"))
if err != nil {
return
}
//查看请求
fmt.Println("请求方法: ", r.Method)
fmt.Println("请求体: ", r.Body)
fmt.Println("请求头", r.Header)
fmt.Println("请求路径:", r.URL)
fmt.Println("客户端地址:", r.RemoteAddr) //包含ip和端口号
}
func main() {
// HandleFunc http请求的处理函数
// http程序启动之后是不会停止的,一直监听请求
// 访问这个url就会触发 helloHandler 函数 (Request) ResponseWriter
// func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
//第一个参数是请求路径,第二个参数是一个函数
http.HandleFunc("/", helloHandler)
fmt.Println("Starting server at :8080")
// func ListenAndServe(addr string, handler Handler) error
// ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。
//如果用户自定义实现了Handler,那么根据相应路径在map中查询到相对应的Handler,然后再调用用户自定义的ServeHTTP处理请求。
//如果用户没有自定义Handler,只注册了对应处理函数(使用了http.HandleFunc),那么就会根据默认DefaultServeMux去map查询到这个函数类型Handler,然后再调用ServeHTTP处理函数。
// 开启监听程序的代码是放在main方法的最后一行的。
if err := http.ListenAndServe("127.0.0.1:8080", nil); err != nil {
fmt.Println("Error starting server:", err)
}
}
浏览器访问
2. HTTP客户端
除了HTTP服务器,Go语言也提供了强大的HTTP客户端功能。以下是一个基本的HTTP客户端示例:
package main
import (
"fmt"
"io"
"net/http"
)
// 手写客户端访问
func main() {
/*
http.Get()
*/
// 一个请求包含 请求方式 请求的url 接收响应结果
// func Get(url string) (resp *Response, err error)
resp, _ := http.Get("http://localhost:8080")
// 通过defer关闭连接 resp.Body 响应的主体
//通过Body来关闭
defer resp.Body.Close()
fmt.Println(resp.Body)
fmt.Println(resp.Status) // 200 OK
fmt.Println(resp.Header) // 响应头
// 接收具体的响应内容,从Body里面获取
//Body是IO流
// The response body is streamed on demand as the Body field is read.
// Body io.ReadCloser
//循环从Body流中读取
buf := make([]byte, 1024)
for {
n, err := resp.Body.Read(buf)
if err != nil && err != io.EOF {
fmt.Println("读取出现了错误")
return
} else {
fmt.Println("读取完毕")
res := string(buf[:n])
fmt.Println("服务器响应的数据为:", res)
break
}
}
}
3. 带参数的请求
客户端编写
- url的参数拼接 ?拼接 & 连接
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// 复杂请求
urlStr := "http://127.0.0.1:8080/login" // ?
// 参数如何拼接到url上,参数封装为数据url.Values{}
data := url.Values{}
//通过url.Values.Set()方法,将参数拼接到url上
data.Set("username", "admin") // ?
data.Set("password", "123456") // ?
// 将url字符串转化为url对象,并给携带数据
// func ParseRequestURI(rawURL string) (*URL, error)
urlNew, _ := url.ParseRequestURI(urlStr)
urlNew.RawQuery = data.Encode()
// http://127.0.0.1:8080/login?password=123456&username=admin
// ? get的传参,多个参数之间使用 & 连接
fmt.Println(urlNew)
//查看类型
fmt.Printf("%T \n", urlNew)
// 发请求,参数是一个字符串地址
resp, _ := http.Get(urlNew.String())
defer resp.Body.Close()
// 读取resp响应信息,一次性读完,返回buf
// func ReadAll(r Reader) ([]byte, error)
buf, _ := io.ReadAll(resp.Body)
fmt.Println(string(buf))
}
服务端:
// http_server.go
package main
import (
"fmt"
"net/http"
)
// 定义发送接收数据函数
func login(resp http.ResponseWriter, req *http.Request) {
// 模拟数据库中存在一个数据
mysqlUserData := "admin"
mysqlPwdData := "123456"
fmt.Println("接收到了login请求")
// 拿到请求中的查询参数
urlData := req.URL.Query()
username := urlData.Get("username")
password := urlData.Get("password")
// 登录逻辑, 将客户端发送的数据和系统数据比对实现登录业务
if username == mysqlUserData {
if password == mysqlPwdData {
resp.Write([]byte("登录成功"))
} else {
resp.Write([]byte("密码错误"))
}
} else {
resp.Write([]byte("登录失败"))
}
}
func main() {
// HandleFunc http请求的处理函数
// http程序启动之后是不会停止的,一直监听请求
// 访问这个url就会触发 helloHandler 函数 (Request) ResponseWriter
// func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
//第一个参数是请求路径,第二个参数是一个函数
http.HandleFunc("/login", login)
fmt.Println("Starting server at :8080")
// func ListenAndServe(addr string, handler Handler) error
// ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。
//如果用户自定义实现了Handler,那么根据相应路径在map中查询到相对应的Handler,然后再调用用户自定义的ServeHTTP处理请求。
//如果用户没有自定义Handler,只注册了对应处理函数(使用了http.HandleFunc),那么就会根据默认DefaultServeMux去map查询到这个函数类型Handler,然后再调用ServeHTTP处理函数。
// 开启监听程序的代码是放在main方法的最后一行的。
if err := http.ListenAndServe("127.0.0.1:8080", nil); err != nil {
fmt.Println("Error starting server:", err)
}
}
登录成功
4. POST请求提交Form表单
客户端
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// 目标URL
urlStr := "http://127.0.0.1:8080/register"
// 构建表单数据
formData := url.Values{}
formData.Set("username", "admin")
formData.Set("password", "123456")
// 发送POST请求
// func PostForm(url string, data url.Values) (resp *Response, err error)
resp, err := http.PostForm(urlStr, formData)
if err != nil {
fmt.Println("Error sending POST request:", err)
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
// 打印响应状态码和响应体
fmt.Println("Response Status:", resp.Status)
fmt.Println("Response Body:", string(body))
}
在这个例子中,我们首先定义了目标URL和表单数据。然后,我们使用 url.Values 类型的 formData 来存储表单键值对,并通过 formData.Set 方法添加数据。最后,我们调用 http.PostForm 函数发送请求,并处理响应。
请注意,http.PostForm 简化了表单数据的发送过程,但如果你需要发送非表单数据(如JSON),你应该使用 http.NewRequest 和 http.Client.Do 方法来构建和发送自定义请求。
此外,http.PostForm 发送的请求体是 application/x-www-form-urlencoded 格式的,这通常用于HTML表单提交,但不适用于所有类型的API请求。
服务端
// http_server.go
package main
import (
"fmt"
"net/http"
)
// 定义发送接收数据函数
func register(resp http.ResponseWriter, req *http.Request) {
fmt.Println("接收到了注册请求")
// 解析表单
// 处理表单的请求, 前端提交表单-后盾解析表单
// func (r *Request) ParseForm() error
err := req.ParseForm()
if err != nil {
return
}
// 获取表单参数 post
username := req.PostForm.Get("username")
password := req.PostForm.Get("password")
fmt.Println(username, password)
// 很多的判断
// 短信
// 验证码
// 存到数据库
resp.Write([]byte("注册成功"))
}
func main() {
// HandleFunc http请求的处理函数
// http程序启动之后是不会停止的,一直监听请求
// 访问这个url就会触发 helloHandler 函数 (Request) ResponseWriter
// func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
//第一个参数是请求路径,第二个参数是一个函数
http.HandleFunc("/register", register)
fmt.Println("Starting server at :8080")
// func ListenAndServe(addr string, handler Handler) error
// ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。
//如果用户自定义实现了Handler,那么根据相应路径在map中查询到相对应的Handler,然后再调用用户自定义的ServeHTTP处理请求。
//如果用户没有自定义Handler,只注册了对应处理函数(使用了http.HandleFunc),那么就会根据默认DefaultServeMux去map查询到这个函数类型Handler,然后再调用ServeHTTP处理函数。
// 开启监听程序的代码是放在main方法的最后一行的。
if err := http.ListenAndServe("127.0.0.1:8080", nil); err != nil {
fmt.Println("Error starting server:", err)
}
}
客户端运行
服务端运行
http.NewRequest 和 http.Client.Do 发送表单请求
客户端:
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
func main() {
// 构建HTTP客户端
client := &http.Client{}
// 定义POST URL
myurl := "http://127.0.0.1:8080/register"
// 定义HTTP负载
data := url.Values{}
data.Set("username", "John Doe")
data.Set("password", "123456")
// 将url.Values编码为表单格式的字符串,并创建一个Reader
payload := strings.NewReader(data.Encode())
// 发送POST请求
// func NewRequest(method, url string, body io.Reader) (*Request, error)
req, err := http.NewRequest("POST", myurl, payload)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
// 设置HTTP消息头
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// 执行HTTP请求
// func (c *Client) Do(req *Request) (*Response, error)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
// 读取响应状态码
fmt.Println("Response Status:", resp.Status)
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
// 打印响应体
fmt.Println("Response Body:", string(body))
}
5. POST请求提交Json数据
有时,我们需要发送JSON格式的POST请求。为此,我们可以使用encoding/json包将数据结构转换为JSON字符串,然后将其作为请求体发送。
客户端:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
func main() {
// 创建一个http.Client对象
client := &http.Client{}
// 构建一个LoginRequest对象
data := LoginRequest{
Username: "zhangsan",
Password: "password",
}
// 将LoginRequest对象转换为JSON字符串
jsonData, err := json.Marshal(data)
if err != nil {
fmt.Println("JSON编码失败:", err)
return
}
// 创建一个请求对象
req, err := http.NewRequest("POST", "http://127.0.0.1:8080/post", bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("创建请求失败:", err)
return
}
// 设置请求头的内容类型为application/json
req.Header.Set("Content-Type", "application/json")
// 发送请求并获取响应
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求发送失败:", err)
return
}
defer resp.Body.Close()
// 读取并处理服务器返回的响应
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取响应失败:", err)
return
}
fmt.Println("响应状态码:", resp.StatusCode)
fmt.Println("响应内容:", string(body))
}
服务端:
// http_server.go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type Result struct {
Username string `json:"username"`
Password string `json:"password"`
}
// 定义发送接收数据函数
func postHandler(resp http.ResponseWriter, req *http.Request) {
// 确保处理的是POST请求
if req.Method != "POST" {
http.Error(resp, "Invalid request method", http.StatusMethodNotAllowed)
return
}
// 读取请求体中的数据
body, err := io.ReadAll(req.Body)
if err != nil {
http.Error(resp, "Error reading request body", http.StatusInternalServerError)
return
}
defer req.Body.Close()
// 打印请求体中的数据
fmt.Printf("Received POST request with body: %s\n", string(body))
//将json字符串反序列化为原生结构体
var result Result
if err := json.Unmarshal(body, &result); err != nil {
http.Error(resp, "Error parsing request body", http.StatusInternalServerError)
}
fmt.Println("客户端发来的数据:", result)
fmt.Printf("客户端发来的数据:%+v\n", result)
fmt.Printf("数据类型%T\n", result)
// 发送响应给客户端
// Received your POST request. This is the response!
resp.Write([]byte("服务端接收到数据"))
}
func main() {
// HandleFunc http请求的处理函数
// http程序启动之后是不会停止的,一直监听请求
// 访问这个url就会触发 helloHandler 函数 (Request) ResponseWriter
// func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
//第一个参数是请求路径,第二个参数是一个函数
http.HandleFunc("/post", postHandler)
fmt.Println("Starting server at :8080")
// func ListenAndServe(addr string, handler Handler) error
// ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。
//如果用户自定义实现了Handler,那么根据相应路径在map中查询到相对应的Handler,然后再调用用户自定义的ServeHTTP处理请求。
//如果用户没有自定义Handler,只注册了对应处理函数(使用了http.HandleFunc),那么就会根据默认DefaultServeMux去map查询到这个函数类型Handler,然后再调用ServeHTTP处理函数。
// 开启监听程序的代码是放在main方法的最后一行的。
if err := http.ListenAndServe("127.0.0.1:8080", nil); err != nil {
fmt.Println("Error starting server:", err)
}
}
运行服务端,客户端,请求成功,服务端拿到json数据。
三、错误处理
在HTTP编程中,错误处理是非常重要的。我们需要优雅地处理各种可能的错误,如网络错误、解析错误、数据库错误等。
下面是一个简单的错误处理示例。
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type ErrorResponse struct {
Error string `json:"error"`
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
// 模拟一个错误
err := fmt.Errorf("something went wrong")
if err != nil {
handleError(w, err)
return
}
fmt.Fprintf(w, "Hello, World!")
}
func handleError(w http.ResponseWriter, err error) {
log.Println("Error:", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(ErrorResponse{Error: err.Error()})
}
func main() {
http.HandleFunc("/", helloHandler)
fmt.Println("Starting server at :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println("Error starting server:", err)
}
}
在这个示例中,我们定义了一个ErrorResponse
结构体,用于表示错误响应。然后,在helloHandler
中,我们模拟了一个错误,并调用handleError
函数来处理该错误。handleError
函数记录了错误日志,设置了响应头和内容类型,并返回了一个JSON格式的错误响应。