后端实践(二):GO语言工程实践课后作业:实现思路、代码以及路径记录|青训营;

77 阅读6分钟

这次的难度比上一个猜数游戏的难度要高一些,原因在于我们需要和外部网络进行连接。和外部网络连接的方式在于接口(API),我们要调用一个接口,获取JSON字符串,实现简易版本的词典。 注:最好有一定项目基础,至少知道前后端再来阅读本文。

项目需求

  1. 调用接口,为翻译提供支持
  2. 使用go的http协议相关内容
  3. 新增额外的词典,更改代码,支持查询

实现思路

  1. 巧妙地使用编程网站,用windows的审查元素得到所需JSON格式。
  2. 用vscode实现整体统合

步骤、代码及路径

获取JSON字符串

寻找网站

本次实验网站:

  1. fanyi.caiyunapp.com/#/
  2. fanyi.baidu.com/

网络元素审查

在网页上点击鼠标右键,如图操作,进行审查元素。

image.png 之后界面如图所示:

image.png

寻找POST格式的JSON

不了解JSON的同学可以看看这个链接:
[JSON的三种格式_json格式_daxiong0816的博客-CSDN博客] (blog.csdn.net/daxiong0816…) 首先,我们要随便输入一个英文单词,让它进行翻译,观察审查元素中“网络”这一栏的变化,然后找到“dict”或者其它相关文件夹。

image.png 点击dict文件夹,我们要找到“标头”的method是POST形式的请求,这和前端ajax相关,感兴趣的同学们可以去搜一搜ajax。

1690516250139.png 在这种情况下,它的载荷是:

1690516339772.png

它的预览是:

1690516385366.png

如果与上述两张图片相同,那么说明位置正确,其中载荷是发送过去的请求,预览是后端传回来的请求。

JSON代码部分

如果要让我们自己来完整这段的写法,那会相当复杂,因此我们可以用另一种方式,即提交给网站,让它来帮忙写。 在提交之前,我们需要先得到网站需要的格式:

image.png

接着,打开代码生成的网址:代码生成,将刚刚复制得到的内容粘贴到上面。 注意,语言选择GO,右上角选择POST才可生成,如下图所示:

1690517198357.png

代码如下:

package main

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

func main() {
	client := &http.Client{}
	var data = strings.NewReader(`msg1=wow&msg2=such&msg3=data`)//是个流
        //req是创建的请求,可以参考ajax相关内容
	req, err := http.NewRequest("POST", "http://fiddle.jshell.net/echo/html/", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Origin", "http://fiddle.jshell.net")
	// req.Header.Set("Accept-Encoding", "gzip, deflate")
	req.Header.Set("Accept-Language", "en-US,en;q=0.8")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Referer", "http://fiddle.jshell.net/_display/")
	req.Header.Set("X-Requested-With", "XMLHttpRequest")
	req.Header.Set("Connection", "keep-alive")
	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字符串
}

但是可能会出现一些由于代码复杂而产生的编译错误,直接删除即可,如果没有错误那就更好了,可以运行,将字符串打印出来。

生成requestBody

需要注意的是,需要什么类型的data就出现什么类型的对象:

1690517942639.png

解析requestBody

我们可以发现,前面出现的结构体非常复杂,如果选择自己来写的话难免会出错,因此,我们同样调用代码生成网站,注意选择转化嵌套,如图所示:

将整个JSON字符串复制下来:

image.png

粘贴并选择嵌套模式

image.png

单击之后右侧改变

1690518361804.png

解析整个结构体

在前文,我们打印了整个JSON字符串,现在要对代码进行修改,JSON字符串BodyTest以结构体中的一个属性值存在,代码如下:

var dictResponse DictResponse
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%#v\n", dictResponse)

打印结果

在打印结果之前,我们可以了解一些关于状态码的知识,如果后端返回的状态码错误,我们一样得不到正确的结果,所以我们应该加上这段代码来判断状态的正确性:

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

此外,我们不能忘记整个结构主体:

fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
	}

新增其它词典支持

本次选择的第二个词典为百度翻译。

1690521906758.png

新增的思路

两个词典可能有所不同:

  1. JSON格式不同
  2. 返回值的BodyRequest不同

因此,我们需要对其进行一定的改动。 老规矩,同样根据生成代码的网站来生成上述的两个代码:

package main

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

func main() {
	client := &http.Client{}
	var data = strings.NewReader(`msg1=wow&msg2=such&msg3=data`)
	req, err := http.NewRequest("POST", "http://fiddle.jshell.net/echo/html/", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Origin", "http://fiddle.jshell.net")
	// req.Header.Set("Accept-Encoding", "gzip, deflate")
	req.Header.Set("Accept-Language", "en-US,en;q=0.8")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Referer", "http://fiddle.jshell.net/_display/")
	req.Header.Set("X-Requested-With", "XMLHttpRequest")
	req.Header.Set("Connection", "keep-alive")
	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)
}

以上是第一个生成代码。

type AutoGenerated struct {
	TransResult struct {
		Data []struct {
			Dst string `json:"dst"`
			Src string `json:"src"`
		} `json:"data"`
		From string `json:"from"`
		To string `json:"to"`
		Status int `json:"status"`
		Type int `json:"type"`
		Phonetic []struct {
			SrcStr string `json:"src_str"`
			TrgStr string `json:"trg_str"`
		} `json:"phonetic"`
	} `json:"trans_result"`
	DictResult struct {
		Edict struct {
			Item []struct {
				TrGroup []struct {
					Tr []string `json:"tr"`
					Example []string `json:"example"`
					SimilarWord []string `json:"similar_word"`
				} `json:"tr_group"`
				Pos string `json:"pos"`
			} `json:"item"`
			Word string `json:"word"`
		} `json:"edict"`
		From string `json:"from"`
		SimpleMeans struct {
			WordName string `json:"word_name"`
			From string `json:"from"`
			WordMeans []string `json:"word_means"`
			Tags struct {
				Core []string `json:"core"`
				Other []string `json:"other"`
			} `json:"tags"`
			Exchange struct {
				WordPl []string `json:"word_pl"`
			} `json:"exchange"`
			Symbols []struct {
				PhEn string `json:"ph_en"`
				PhAm string `json:"ph_am"`
				Parts []struct {
					Part string `json:"part"`
					Means []string `json:"means"`
				} `json:"parts"`
				PhOther string `json:"ph_other"`
			} `json:"symbols"`
		} `json:"simple_means"`
		Common struct {
			Text string `json:"text"`
			From string `json:"from"`
		} `json:"common"`
		GeneralKnowledge struct {
			SimilarWords []struct {
				En string `json:"en"`
				Zh string `json:"zh"`
			} `json:"similar_words"`
			WordName string `json:"word_name"`
			WordLang string `json:"word_lang"`
			WordType string `json:"word_type"`
		} `json:"general_knowledge"`
		Collins struct {
			Entry []struct {
				EntryID string `json:"entry_id"`
				Type string `json:"type"`
				Value []struct {
					MeanType []struct {
						InfoType string `json:"info_type"`
						InfoID string `json:"info_id"`
						Example []struct {
							ExampleID string `json:"example_id"`
							TtsSize string `json:"tts_size"`
							Tran string `json:"tran"`
							Ex string `json:"ex"`
							TtsMp3 string `json:"tts_mp3"`
						} `json:"example"`
					} `json:"mean_type"`
					Gramarinfo []interface{} `json:"gramarinfo"`
					Tran string `json:"tran"`
					Def string `json:"def"`
					MeanID string `json:"mean_id"`
					Posp []struct {
						Label string `json:"label"`
					} `json:"posp"`
				} `json:"value"`
			} `json:"entry"`
			WordName string `json:"word_name"`
			Frequence string `json:"frequence"`
			WordEmphasize string `json:"word_emphasize"`
			WordID string `json:"word_id"`
		} `json:"collins"`
		Lang string `json:"lang"`
		Oxford struct {
			Entry []struct {
				Tag string `json:"tag"`
				Name string `json:"name"`
				Data []struct {
					Tag string `json:"tag"`
					Data []struct {
						Tag string `json:"tag"`
						P string `json:"p"`
						PText string `json:"p_text"`
					} `json:"data"`
				} `json:"data"`
			} `json:"entry"`
			Unbox string `json:"unbox"`
		} `json:"oxford"`
		Sanyms []struct {
			Tit string `json:"tit"`
			Data []struct {
				P string `json:"p"`
				D []string `json:"d"`
			} `json:"data"`
			Type string `json:"type"`
		} `json:"sanyms"`
		Usecase struct {
			Idiom []struct {
				P string `json:"p"`
				Tag string `json:"tag"`
				Data []struct {
					Tag string `json:"tag"`
					Data []struct {
						EnText string `json:"enText"`
						Tag string `json:"tag"`
						ChText string `json:"chText"`
						Before []struct {
							Tag string `json:"tag"`
							Data []struct {
								RText string `json:"r_text"`
								R string `json:"r"`
								Tag string `json:"tag"`
							} `json:"data"`
						} `json:"before,omitempty"`
					} `json:"data"`
				} `json:"data"`
			} `json:"idiom"`
		} `json:"usecase"`
		BaiduPhrase []struct {
			Tit []string `json:"tit"`
			Trans []string `json:"trans"`
		} `json:"baidu_phrase"`
		Synonym []struct {
			Synonyms []struct {
				Tips string `json:"tips"`
				Bx string `json:"bx"`
				Syn struct {
					P string `json:"p"`
					Word string `json:"word"`
					D []string `json:"d"`
				} `json:"syn"`
				Ex []struct {
					EnText string `json:"enText"`
					ChText string `json:"chText"`
				} `json:"ex"`
			} `json:"synonyms"`
			Guide string `json:"guide"`
			Words []string `json:"words"`
		} `json:"synonym"`
		QueryExplainVideo struct {
			ID int `json:"id"`
			UserID string `json:"user_id"`
			UserName string `json:"user_name"`
			UserPic string `json:"user_pic"`
			Query string `json:"query"`
			Direction string `json:"direction"`
			Type string `json:"type"`
			Tag string `json:"tag"`
			Detail string `json:"detail"`
			Status string `json:"status"`
			SearchType string `json:"search_type"`
			FeedURL string `json:"feed_url"`
			Likes string `json:"likes"`
			Plays string `json:"plays"`
			CreatedAt string `json:"created_at"`
			UpdatedAt string `json:"updated_at"`
			DuplicateID string `json:"duplicate_id"`
			RejectReason string `json:"reject_reason"`
			CoverURL string `json:"coverUrl"`
			VideoURL string `json:"videoUrl"`
			ThumbURL string `json:"thumbUrl"`
			VideoTime string `json:"videoTime"`
			VideoType string `json:"videoType"`
		} `json:"queryExplainVideo"`
	} `json:"dict_result"`
	LijuResult struct {
		Double string `json:"double"`
		Tag []string `json:"tag"`
		Single string `json:"single"`
	} `json:"liju_result"`
	Logid int `json:"logid"`
}

以上是第二段结构体的生成代码。

我们发现,这两个代码与之前的彩云翻译有所区别,甚至区别不小。因此,不能让一个请求搞定两个翻译的格式。 由此,我提出了另一个想法,那就是准备两个请求,一个命名为req1,另一个为req2,同时按照各自的格式进行请求。 这个“同时”的代码需要用到go语言的并行设置,见之后的笔记。