目录
Day 7: 打通外部世界 - HTTP客户端与JSON解析实战
在学习Go语言的过程中,标准库是我们最常用的工具之一。它提供了丰富的功能,涵盖文件操作、数据序列化、网络请求等多个方面,能够显著提升开发效率。本文详细介绍Go标准库中文件操作(os
、io
、ioutil
)、JSON处理(encoding/json
)以及HTTP客户端(GET/POST请求)的使用方法,并通过一个实际练习——编写天气查询CLI工具——来巩固所学知识。
一、文件操作
文件操作是编程中的基础技能之一,Go语言标准库提供了多个包来支持文件和目录的处理,包括os
、io
和ioutil
。值得注意的是,在Go 1.16及之后的版本中,ioutil
包的部分功能被迁移到了os
和io
包中。
1.1 os
包:与操作系统交互
os
包提供了与操作系统交互的基本函数,支持文件的创建、打开、读写和删除等操作。以下是一些常用函数:
- 创建文件:
os.Create(name string) (*File, error)
创建一个指定名称的文件,如果文件已存在,会清空其内容。 - 打开文件:
os.Open(name string) (*File, error)
以只读模式打开文件。 - 写入文件:
file.Write(b []byte) (n int, err error)
向文件中写入字节数据。 - 读取文件:
file.Read(b []byte) (n int, err error)
从文件中读取数据到字节切片。 - 删除文件:
os.Remove(name string) error
删除指定文件。
1.2 io
包:I/O原语
io
包提供了底层的输入输出接口,例如Reader
和Writer
,适合处理数据流。常用函数包括:
- 复制数据:
io.Copy(dst Writer, src Reader) (written int64, err error)
将数据从源读取器复制到目标写入器。 - 读取所有数据:
io.ReadAll(r Reader) ([]byte, error)
从读取器中读取所有数据,返回字节切片。
1.3 ioutil
包(Go 1.16之前)
在Go 1.16之前,ioutil
包提供了一些便捷的工具函数,例如:
- 读取文件:
ioutil.ReadFile(filename string) ([]byte, error)
一次性读取整个文件内容。 - 写入文件:
ioutil.WriteFile(filename string, data []byte, perm os.FileMode) error
将数据写入文件并设置权限。
注意:在Go 1.16及之后,推荐使用os.ReadFile
和os.WriteFile
替代上述ioutil
函数。
1.4 示例:文件读写操作
以下是一个简单的示例,展示如何创建文件、写入数据并读取内容:
package main
import (
"fmt"
"os"
)
func main() {
// 创建并写入文件
file, err := os.Create("example.txt")
if err != nil {
fmt.Println("创建文件失败:", err)
return
}
defer file.Close() // 确保文件在函数结束时关闭
_, err = file.Write([]byte("Hello, Go!"))
if err != nil {
fmt.Println("写入文件失败:", err)
return
}
// 读取文件内容
data, err := os.ReadFile("example.txt")
if err != nil {
fmt.Println("读取文件失败:", err)
return
}
fmt.Println(string(data)) // 输出: Hello, Go!
}
二、JSON处理
JSON是一种轻量级的数据交换格式,常用于API响应和配置文件。Go语言的encoding/json
包提供了强大的JSON编码和解码功能。
2.1 编码(Marshal)
将Go数据结构(如结构体、map、slice)转换为JSON字符串:
func Marshal(v interface{}) ([]byte, error)
2.2 解码(Unmarshal)
将JSON字符串解析为Go数据结构:
func Unmarshal(data []byte, v interface{}) error
2.3 示例:JSON编码与解码
以下示例展示如何将结构体编码为JSON字符串,并将JSON字符串解码回结构体:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"` // 使用标签指定JSON字段名
Age int `json:"age"`
}
func main() {
// 编码
p := Person{Name: "Alice", Age: 30}
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("JSON编码失败:", err)
return
}
fmt.Println(string(jsonData)) // 输出: {"name":"Alice","age":30}
// 解码
var p2 Person
err = json.Unmarshal(jsonData, &p2)
if err != nil {
fmt.Println("JSON解码失败:", err)
return
}
fmt.Printf("%+v\n", p2) // 输出: {Name:Alice Age:30}
}
通过结构体标签(如json:"name"
),我们可以自定义JSON字段名,使编码和解码更加灵活。
三、HTTP客户端
Go语言的net/http
包提供了强大的HTTP客户端功能,支持发送GET、POST等请求。
3.1 GET请求
使用http.Get
发送简单的GET请求:
resp, err := http.Get(url string)
3.2 POST请求
使用http.Post
发送POST请求,需指定内容类型和请求体:
resp, err := http.Post(url string, contentType string, body io.Reader)
3.3 自定义请求
使用http.NewRequest
创建自定义请求,支持多种HTTP方法:
req, err := http.NewRequest(method string, url string, body io.Reader)
3.4 示例:发送GET请求
以下示例展示如何发送GET请求并读取响应内容:
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
resp, err := http.Get("https://api.example.com/data")
if err != nil {
fmt.Println("HTTP GET请求失败:", err)
return
}
defer resp.Body.Close() // 确保响应体关闭
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取响应体失败:", err)
return
}
fmt.Println(string(body))
}
四、练习:编写天气查询CLI工具
为了将文件操作、JSON处理和HTTP客户端的知识结合起来,我们将实现一个简单的命令行工具,用于查询指定城市的天气信息。
4.1 需求分析
- 用户通过命令行输入城市名称。
- 程序向天气API发送HTTP GET请求。
- 解析API返回的JSON数据。
- 显示天气信息(如温度、湿度、天气描述)。
4.2 实现步骤
a. 选择天气API
我们使用免费的OpenWeatherMap API。你需要注册并获取API密钥(API Key)。请求URL格式如下:
http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}
b. 定义JSON结构体
根据API返回的JSON结构,定义对应的Go结构体。例如:
{
"main": {
"temp": 298.15,
"humidity": 65
},
"weather": [
{
"description": "clear sky"
}
]
}
对应的Go结构体:
type WeatherResponse struct {
Main struct {
Temp float64 `json:"temp"`
Humidity int `json:"humidity"`
} `json:"main"`
Weather []struct {
Description string `json:"description"`
} `json:"weather"`
}
c. 完整实现
以下是完整的天气查询CLI工具代码:
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type WeatherResponse struct {
Main struct {
Temp float64 `json:"temp"`
Humidity int `json:"humidity"`
} `json:"main"`
Weather []struct {
Description string `json:"description"`
} `json:"weather"`
}
func main() {
// 检查命令行参数
if len(os.Args) < 2 {
fmt.Println("请提供城市名称,例如:go run main.go Beijing")
return
}
city := os.Args[1]
// 构造API请求
apiKey := "YOUR_API_KEY" // 替换为你的API密钥
url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s", city, apiKey)
// 发送GET请求
resp, err := http.Get(url)
if err != nil {
fmt.Println("HTTP GET请求失败:", err)
return
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
fmt.Println("API请求失败,状态码:", resp.StatusCode)
return
}
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取响应体失败:", err)
return
}
// 解析JSON
var weather WeatherResponse
err = json.Unmarshal(body, &weather)
if err != nil {
fmt.Println("JSON解码失败:", err)
return
}
// 显示天气信息
fmt.Printf("城市: %s\n", city)
fmt.Printf("温度: %.2f°C\n", weather.Main.Temp-273.15) // 开尔文转摄氏度
fmt.Printf("湿度: %d%%\n", weather.Main.Humidity)
fmt.Printf("天气: %s\n", weather.Weather[0].Description)
}
4.3 运行与测试
- 将
YOUR_API_KEY
替换为你的OpenWeatherMap API密钥。 - 编译并运行程序:
go run main.go Beijing
- 示例输出:
城市: Beijing 温度: 25.00°C 湿度: 65% 天气: clear sky
五、总结
通过本文,我们深入学习了Go语言标准库中文件操作(os
、io
、ioutil
)、JSON处理(encoding/json
)和HTTP客户端(net/http
)的使用方法,并通过一个天气查询CLI工具的练习将这些知识融会贯通。掌握这些技能后,你将能够更高效地处理文件、解析数据和调用网络API。