基于Golang的命令行在线词典实现与解析 | 青训营

229 阅读4分钟

介绍

本程序为字节跳动后端训练营中的案例,通过彩云小译API实现翻译功能。

原理

我们使用POST请求将需要翻译的字符串通过相应的彩云小译的接口发送到其服务端,服务端把接收到的字符串翻译后再向客户端响应结果。

通过解析彩云小译的response body(彩云小译的前后端使用json格式进行通信),并做一定的处理,最终将翻译结果按期望的格式输出。

获取彩云小译API

我们进入彩云小译网站,按F12弹出开发者工具,选择Network(网络),开发者工具会开始记录network activity(网络活动)。 image.png

在网页的左侧白框中随便输入一些文字,例如“good”,点击翻译按钮后右侧框出现翻译结果。 image.png

此时在开发者工具中可以看到多出了一些项目(你的结果可能与图中不一致),其中名字为dist的POST请求就是我们需要的。我们只要仿照这个POST请求来构建我们自己的请求,就可以向服务端发送我们需要翻译的字符串了。 image.png

生成发送请求的Golang代码

使用Golang的http标准库,我们就可以向服务端发送我们的请求。但是由于请求头比较复杂,直接手写请求会比较麻烦,因此我们可以使用代码生成来帮助我们自动生成请求部分的Go代码。

在dist项目上右键点击Copy -> Copy as cURL,将cURL复制到剪贴板。复制的内容其实是curl command。我们可以将剪贴板里的内容粘贴到终端中执行,得到request body。

Screenshot from 2023-07-25 19-48-22.png

curl 'https://api.interpreter.caiyunai.com/v1/dict' \
  -H 'authority: api.interpreter.caiyunai.com' \
  -H 'accept: application/json, text/plain, */*' \
  // 省略...
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKi
  
  --data-raw '{"trans_type":"en2zh","source":"good"}' \
  --compressed

这里我们使用curlconverter来自动生成请求部分的代码,这个网站可以直接将curl的请求转换成各种语言相应的代码。 image.png

思考:
可以看到请求头中还有翻译类型的字段(en2zh表示英译中),所以我猜测判断输入文字的语言这个功能是由前端完成的。

生成代码解析

// 省略...

func main() {
    client := &http.Client{}
    // 将json格式字串转换为流?
    var data = strings.NewReader(`{"trans_type":"zh2en","source":"好"}`)
    // 生成一个请求,参数分别为请求类型,请求URL,
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    if err != nil {
        log.Fatal(err)
    }
        
    // 设置请求头
    req.Header.Set("authority", "api.interpreter.caiyunai.com")
    req.Header.Set("accept", "application/json, text/plain, */*")
    // 省略...
	
    // 发送请求
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
        
    // 从响应体中读取
    bodyText, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
        
    fmt.Printf("%s\n", bodyText)
}

运行上述代码,就会得到服务端响应的一长串json格式字串。

request body

在生成的代码中,发送的请求中的数据是硬编码的,而我们希望用户可以在程序运行时输入需要翻译的文字。为了实现这个需求,我们可以将用户的输入保存在一个结构体的实例中,再将该实例序列化为json字符串。

首先,我们构建一个与request body有着相同结构的结构体:

type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"sourse"`
}

利用golang标准库中的encoding/json包,可以轻松地将该结构体的实例序列化为json格式:

request := DictRequest{TransTyep: "en2zh", Source: "good"}
buf, err := json.Marshal(request)

接下来只需要理输入,并将用户的输入保存至DictRequest的一个实例中即可。

解析response body

服务端返回的信息也是以json格式编码的,含有多个字段,我们需要对其进行解析,并提取出我们需要的字段。处理这样的response body有多种方式,而在Golang这种强类型语言中,较好的实践方法是建立一个与response body结构相同的结构体来接住它。但由于response body的结构十分复杂,我们同样可以使用代码生成来帮助我们完成这个步骤。

使用oktools这个网站来根据response body的json格式生成我们需要的结构体。我们将刚刚发送请求后返回的json格式字符串复制到该网站中,得到相应的golang结构体。将该结构体重命名为dictResponse,然后就可以将respose body反序列化并保存到该结构体的一个实例中。

bodyText, err := ioutil.ReadAll(resp.Body)
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
    log.Fatal(err)
}

然后我们只要根据自己的需求,输出dictResponse结构体中相应的字段即可。

优化

1.防御式编程

if resp.StatusCode != 200 {
    log.Fatal("bad StatusCode:", resp.StatusCode, "body", string)
}