Go语言实现词典|青训营

66 阅读6分钟

Go语言调用第三方api实现词典

对于词典,顾名思义应该拥有一个庞大的数据库存放各种单词的释义、用法等,而如果在本地我们没有这样的资源,我们可以尝试调用第三方API,调取我们所需要的数据。 此处我们借助“彩云小译”fanyi.caiyunapp.com/#/ 在线翻译提供的接口实现

Go发送http请求

http请求

  • 对于通过http通信的web应用,想调用在线翻译的接口就要向其发送http请求。我们来到上述网址并随意查找一个单词,利用浏览器自带的检查工具(右键->检查,或者F12)查看http网络数据包的信息。 此处我们查询单词better并点击翻译按钮,点击翻译按钮即向服务端发送了http请求,我们利用检查工具可以看到,发生了以下交互

图中出现了“预检”字样的请求,此处简单解释一下预检(不是本文重点,不感兴趣可跳过):当浏览器发起跨域请求时,如果满足以下条件之一,浏览器会发送预检请求:

  1. 使用了简单请求方法:对于跨域请求,如果请求使用了非常见的HTTP方法,如PUT、DELETE、PATCH,浏览器会发送预检请求。这是为了确保服务器支持这些方法并允许客户端发起请求
  2. 存在自定义请求头:如果请求中携带了自定义的请求头信息(与CORS规范中的简单请求头不同),浏览器会发送预检请求。预检请求可以检查服务器是否允许这些自定义请求头,以保证安全性
  3. 使用了不支持CORS的安全模式:当请求中包含了非简单请求头字段,并且请求中的credentials属性被设置为true(例如通过XMLHttpRequest的withCredentials属性设置为true),浏览器会发送预检请求。这是为了防止跨域请求的潜在安全风险,要求服务器确认是否允许使用credentials属性进行验证。

预检请求是一个OPTIONS请求,其请求头中会包含原始请求的相关信息,例如请求方法、请求头字段等。服务器接收到预检请求后,会检查 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 等字段,判断是否允许跨域请求。如果服务器认可该请求,会返回一个 HTTP 200 响应,其中会包含允许的请求头、请求方法等信息。此时,浏览器才会继续发送真实请求,否则请求会被阻止,并抛出错误。

需要注意的是,预检请求会增加请求的延迟和带宽开销,因此对于跨域请求,应尽量减少使用非简单请求方法和自定义请求头,以避免触发预检请求

在图中我们发现了dict字样的请求,点开查看(注意:此处的请求方法应为POST,而不是OPTIONS,OPTIONS请求即为上文提到的预检请求) 在这我们可以看到http请求头的详细信息

  1. 首先能看到请求网址api.interpreter.caiyunai.com/v1/dict 从网址名称就能够猜测,这是后端留有的API接口
  2. 其次看到请求方法为POST请求(感兴趣的可以了解RESTFUL风格中几种主要的请求方式)
  3. 状态码200,成功交互的标志

随后我们分别点击载荷和预览选项卡,可以看到该请求携带的数据信息和返回响应

image.png

image.png

至此,我们大概了解了这个web应用中翻译单词功能实现的大致流程:

  1. 用户在网页输入单词并点击翻译,向服务端发送POST请求
  2. 服务端收到POST请求并按要求返回响应
  3. web端根据返回的响应将数据信息渲染展示给用户

Go发送http请求

那么我们借助该提供的api同样实现词典查询功能,首先就是要模拟web向后端接口地址发送http请求的过程。但是经过上面的阅读可以发现这个请求非常的复杂,如果人工构造非常的麻烦所以我们利用代码生成工具,进行请求的构造

代码生成工具

利用在线代码生成工具:curlconverter.com/go/

  1. 首先在浏览器检查工具中复制请求信息,右键dict请求:
  2. 打开代码生成工具,粘贴至curl command框中,下方就会生成Go语言代码

可以看到生成的代码非常长,我选取其中关键代码进行注释:

  • 创建请求:req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
  • 设置请求头:req.Header.Set("authority", "api.interpreter.caiyunai.com")......
  • 发送请求:resp, err := client.Do(req)
  • 读取响应:bodyText, err := io.ReadAll(resp.Body)

此时将上述代码运行应该可以在控制台输出得到一串json格式的字符串。

但是至此,我们的输出是固定的,也就是我们的请求数据是写死的,所以接下来我们需要将请求数据中需要通过读取用户输入的部分设置为变量。

var data = strings.NewReader(`{"trans_type":"en2zh","source":"better"}`) 
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)

从以上代码段我们可以看到,在创建请求的过程中我们的请求数据data,在声明是就定义为{"trans_type":"en2zh","source":"better"}。显然我们需要从这入手修改参数。

Go解析json

不难发现,数据格式采用json。所以我们需要用Go语言对json进行解析。

  • 首先创建json格式对应的结构体:
  1. 发送请求时的数据结构如下:
type DictRequest struct {  
    TransType string `json:"trans_type"`  
    Source string `json:"source"`  
    UserID string `json:"user_id"`  //可选,此处暂时用不上
}
  1. 同时我们还需要定义返回响应的数据结构以解析返回响应,但是当我们从浏览器中看到返回响应的数据结构时难免会头疼,因为这是一个巨大的json image.png 如果仍然手动创建结构体那一定是一个浩大的工程,所以这个时候又需要用到代码生成工具,这次我们使用 oktools.net/json2go 将JSON转为GoLand。1)在左边粘贴json数据;2)点击转换-嵌套;3)得到右边的GoLand。 image.png

  2. 定义好数据结构后,我们就可以使用Go语言的json库函数MarshalUnmarshal,对json进行解析。解析完成后根据数据结构选取出我们需要的字段输出即可。

封装函数:接收用户输入,发送请求

利用上述封装好的函数setHttpPost,fmt.Scanln读取用户输入的英语单词 -> 构造请求数据 -> 传入请求函数 -> 输出请求响应。即完成了我们的词典

func Dictionary() {  
    var word string   
    for {  
        fmt.Println("英语单词字典:")  
        fmt.Scanln(&word)  
        request := DictRequest{TransType: "en2zh", Source: word}  
        setHttpPost(request)  
    }  
}

运行结果

image.png