GO语言工程实践课后作业:实现思路、代码以及路径记录 | 豆包MarsCode AI刷题

84 阅读5分钟

GO语言实战案例(二)


simpleDict

利用Go语言开发一个简单的单词查询工具simpleDict,并在多个版本中对其进行逐步优化。这个工具通过访问彩云翻译API获取单词的翻译和解释


版本 1:基本的单词查询实现

在第一个版本中,我们通过最基本的HTTP请求实现了对API的调用。代码功能主要集中在发送请求、设置请求头、以及打印响应内容上。

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func main() {
	// 初始化HTTP客户端
	client := &http.Client{}

	// 定义请求内容
	var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)

	// 创建一个新的POST请求
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}

	// 设置请求头信息
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")

	// 发送请求并接收响应
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	// 读取并打印响应内容
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", bodyText)
}

在这个版本中,代码结构较为简单,主要实现了以下内容:

  • 定义HTTP客户端并创建请求。
  • 设置必要的请求头。
  • 发起请求并读取响应内容。

优化方向:这个版本存在硬编码问题,比如请求的JSON数据和API密钥是写死在代码中的,不便于复用和维护。

版本 2:引入结构体定义请求参数

第二版的代码通过引入结构体DictRequest来封装请求参数,简化了请求体的生成。这样我们可以直接将结构体序列化为JSON,而不是手动拼接请求内容。

主要变化代码

type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}

func main() {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: "good"}

	// 将结构体编码为JSON格式的请求体
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)

	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	// ...
}

改进点

  • 通过结构体定义请求参数,使得代码更具扩展性。
  • 使用json.Marshal自动将结构体转换为JSON,避免手动拼接字符串的错误。

这种方式更符合Go语言的类型安全性和结构化编程特点,同时代码更加简洁明了。

版本 3:定义响应结构体并解析响应内容

在版本3中,我们添加了DictResponse结构体,用于解析API返回的JSON数据。通过使用json.Unmarshal,可以将响应内容直接映射到结构体中,便于后续处理和使用。

主要变化代码

type DictResponse struct {
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string `json:"explanations"`
	} `json:"dictionary"`
}

func main() {
	// 省略请求部分...
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// 解析响应JSON到结构体
	var dictResponse DictResponse
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%#v\n", dictResponse)
}

改进点

  • 使用结构体解析响应JSON数据,简化了数据访问方式,不再需要手动解析字段。
  • 增强了代码的可靠性和可读性,减少了对JSON字段的硬编码访问。

这种结构化的解析方式使得代码更易于维护,Go的json包也提供了较为高效和便捷的解析支持。

版本 4:将请求逻辑封装为函数并支持命令行输入

在最后的版本中,我们将查询逻辑封装为query函数,并使用os.Args读取命令行输入。这一变化使得代码不仅更具复用性,也使其更贴近实际的CLI工具应用。

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

// 定义请求的结构体,封装请求参数
type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}

// 定义响应的结构体,封装响应数据
type DictResponse struct {
	Dictionary struct {
		Prons struct {           // 发音信息
			EnUs string `json:"en-us"`   // 美式发音
			En   string `json:"en"`      // 英式发音
		} `json:"prons"`
		Explanations []string `json:"explanations"`  // 单词解释
	} `json:"dictionary"`
}

// 定义查询函数,封装查询逻辑
func query(word string) {
	client := &http.Client{}
	// 设置请求内容
	request := DictRequest{TransType: "en2zh", Source: word}

	// 序列化请求体为 JSON
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)

	// 创建 POST 请求
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}

	// 设置请求头信息
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")

	// 发送请求并接收响应
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()  // 确保资源释放

	// 读取并解析响应体
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// 检查状态码确保请求成功
	if resp.StatusCode != 200 {
		log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
	}

	// 解析 JSON 响应到结构体
	var dictResponse DictResponse
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}

	// 输出查询结果:发音与释义
	fmt.Println("单词:", word)
	fmt.Println("英式发音:", dictResponse.Dictionary.Prons.En)
	fmt.Println("美式发音:", dictResponse.Dictionary.Prons.EnUs)
	fmt.Println("释义:")
	for _, explanation := range dictResponse.Dictionary.Explanations {
		fmt.Println(" -", explanation)
	}
}

func main() {
	// 检查命令行参数
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, "使用方法: simpleDict WORD")
		fmt.Fprintln(os.Stderr, "示例: simpleDict hello")
		os.Exit(1)
	}
	// 提取命令行输入的单词
	word := os.Args[1]
	// 调用查询函数
	query(word)
}

改进点

  • 将核心查询逻辑封装到query函数中,提高了代码的复用性。
  • 支持通过命令行参数输入单词,提升了CLI工具的实用性。
  • os.Args的使用符合Go语言开发CLI工具的简便特性,进一步提升了代码的灵活性。

总结与思考

通过四个版本的演进,这段代码不仅完成了从功能实现到结构优化的过程,也充分展示了Go语言的编程优势:

  1. 结构体的灵活性:通过结构体封装请求和响应数据,简化了数据的处理流程,增强了代码的可读性和维护性。
  2. 内置库的强大支持:Go语言标准库为HTTP请求、JSON解析、错误处理等功能提供了高效的支持,使得代码可以避免外部依赖。
  3. 简洁的错误处理机制:Go的错误处理方式确保了代码执行的稳定性,通过显式的错误检查使代码流程清晰。

在Go语言中,我们能以简洁的方式实现高效、健壮的程序。通过这种逐步优化的方式,不仅实现了代码的功能扩展,也提升了代码的可维护性和可读性。