这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记。
项目地址&实现
地址:github.com/wangkechun/…
实现:抓取彩云翻译传送至服务器的curl请求,转为GO语言,在 GOLANG中接收返回并且使用流、序列化等函数,在GOLANG中实现在线翻译功能。
step1:抓取curl转为GO语言
右击彩云小译 - 在线翻译 (caiyunapp.com)页面-检查-网络-dict(请求方法POST)-右击复制为CURL。粘贴至curlconverter.com/ 获得该请求的GO语言形式。 结果大致如下:
client := &http.Client{}
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
\\一堆请求头...
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)
首先创建了请求,由于NewRequest的第三个参数类型是流,我们需要将命令转化为流的形式,即
var data = strings.NewReader({"trans_type":"en2zh","source":"good"})。同时,如果遇到body较大的情况,流可以起到节省内存的效果。然后提交请求resp, err := client.Do(req)resp就是得到的结果。因为得到的结果同样也是一个流,为了避免内存泄漏,我们需要得到结果之后马上关闭。在此调用了defer resp.Body.Close()defer后面的函数只有在当前函数执行完毕后才能执行,通常用于释放资源。即defer之后的所有命令执行完毕后才会执行close的命令。最后将得到的json流以byte数组的形式读取到内存里面,即bodyText, err := ioutil.ReadAll(resp.Body)。
step2:JSON序列化
为了能实现输入单词翻译,而不是改变字符串中的good为单词进行翻译。我们需要创建一个结构体,其结构和json一一对应,其结构体有一个string类型的word,可以让我们传入,再将其序列化为json格式发送请求。( 如果变量打上了json标签,如TransType旁边的 `json:"trans_type"` ,那么转化成的json key就用该标签“name”,否则取变量名作为key。)
结构体:
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
然后再把原先CURL转化的GO语言稍作改动。 创建请求后,先创建一个结构体,然后将该结构体编码成json字符串。
request := DictRequest{TransType: "en2zh", Source: "good"}
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)
if err != nil {
log.Fatal(err)
}
step3:获取结果
首先在右击彩云小译 - 在线翻译 (caiyunapp.com)页面-检查-网络-dict(请求方法POST)-响应 可以查看返回的JSON的格式。将此格式复制到JSON转Golang Struct - 在线工具 - OKTools点击转换-嵌套得到与JSON结构一一对应的结构体。
我们将此结构体粘贴到代码中,并更名为DictResponse。我们便可以把得到的JSON格式的resp读取到结构体中,挑选合适的信息进行输出。
首先去掉fmt.Printf("%s\n", bodyText)。添加以下代码
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n", dictResponse)
大意为新建一个DictResponse类型的变量dictResponse用于接收信息。通过json.Unmarshal函数,将bodyText反序列化(JSON->结构体格式)并存入dictResponse中。然后我们就可以通过结构体的字段名访问信息。即去掉fmt.Printf("%#v\n", dictResponse)添加:
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
仅取得音标和翻译结果的字段。
step4:翻译函数化&避免错误
我们将main函数改名为query,并且让其接收string类型的word参数。再将request := DictRequest{TransType: "en2zh", Source: "good"}改为request := DictRequest{TransType: "en2zh", Source: word}便可以实现翻译传入的相应单词。
再新建一个main函数,内容如下
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
`)
os.Exit(1)
}
word := os.Args[1]
query(word)
}
获取输入的单词然后赋值给word,将其传入query即可实现翻译。完成了翻译函数化,使主函数简洁不臃肿。
最后在query函数var dictResponse DictResponse的上方加上
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
当网络,服务器出现问题时,resp会出现错误(不是200的时候),即可发现错误。
小结
通过此项目,对JSON传输格式,序列化,流,defer函数的作用,发送请求的机制有一定的了解。本文写作比较匆忙,如果有疑问或者错误请不吝赐教。