注意:本文假定读者熟悉Go语法和术语,并且介于初学者和中级之间。

让我们首先运行该应用程序,然后在运行中进行查看-

$ go-grab-xkcd --help

Usage of go-grab-xkcd:
-n int
Comic number to fetch (default latest)
-o string
Print output in format: text/json (default "text")
-s Save image to current directory
-t int
Client timeout in seconds (default 30)
$ go-grab-xkcd -n 323

Title: Ballmer Peak
Comic No: 323
Date: 1-10-2007
Description: Apple uses automated schnapps IVs.
Image: https://imgs.xkcd.com/comics/ballmer_peak.png
$ go-grab-xkcd -n 323 -o json
{
"title": "Ballmer Peak",
"number": 323,
"date": "1-10-2007",
"description": "Apple uses automated schnapps IVs.",
"image": "https://imgs.xkcd.com/comics/ballmer_peak.png"
}
您可以通过下载并运行计算机上的应用程序来尝试其他选项。

在本教程结束后,您将熟悉以下主题:

接受命令行参数
JSON和Go结构之间的相互转换
进行API调用
创建文件(从Internet下载和保存)
字符串操作
以下是项目结构-

$ tree go-grab-xkcd
go-grab-xkcd
├── client
│ └── xkcd.go
└── model
└── comic.go
├── main.go
└── go.mod
go.mod-围棋模块围棋用于包管理文件
main.go -应用程序的主要入口点
comic.go-将数据表示为struct并对其进行操作
xkcd.go -xkcd客户端,用于对API进行HTTP调用,解析响应并将其保存到磁盘
1:初始化项目
创建一个go.mod文件-

$ go mod init
这将有助于包管理(考虑JS中的package.json)。

2:xkcd API
xkcd很棒,您不需要任何注册或访问密钥即可使用其API。打开xkcd API“文档”,您会发现有2个端点-

http://xkcd.com/info.0.json -获取最新漫画
http://xkcd.com/614/info.0.json -通过漫画编号获取特定漫画
以下是来自这些端点的JSON响应-

{
"num": 2311,
"month": "5",
"day": "25",
"year": "2020",
"title": "Confidence Interval",
"alt": "The worst part is that's the millisigma interval.",
"img": "https://imgs.xkcd.com/comics/confidence_interval.png",
"safe_title": "Confidence Interval",
"link": "",
"news": "",
"transcript": ""
}
相关xkcd

2:为漫画创建模型
基于上述JSON响应,我们创建了一个struct名为ComicResponse在comic.go里面model包

type ComicResponse struct {
Month string

json:"month"

Num int
json:"num"

Link string
json:"link"

Year string
json:"year"

News string
json:"news"

SafeTitle string
json:"safe_title"

Transcript string
json:"transcript"

Alt string
json:"alt"

Img string
json:"img"

Title string
json:"title"

Day string
json:"day"

}
您可以使用JSON-to-Go工具从JSON自动生成结构。

还创建另一个结构,该结构将用于从我们的应用程序输出数据。

type Comic struct {
Title string

json:"title"

Number int
json:"number"

Date string
json:"date"

Description string
json:"description"

Image string
json:"image"

}
将以下两种方法添加到ComicResponsestruct-

// FormattedDate formats individual date elements into a single string
func (cr ComicResponse) FormattedDate() string {
return fmt.Sprintf("%s-%s-%s", cr.Day, cr.Month, cr.Year)
}
// Comic converts ComicResponse that we receive from the API to our application's output format, Comic
func (cr ComicResponse) Comic() Comic {
return Comic{
Title: cr.Title,
Number: cr.Num,
Date: cr.FormattedDate(),
Description: cr.Alt,
Image: cr.Img,
}
}
然后将以下两种方法添加到Comicstruct-

// PrettyString creates a pretty string of the Comic that we'll use as output
func (c Comic) PrettyString() string {
p := fmt.Sprintf(
"Title: %s\nComic No: %d\nDate: %s\nDescription: %s\nImage: %s\n",
c.Title, c.Number, c.Date, c.Description, c.Image)
return p
}
// JSON converts the Comic struct to JSON, we'll use the JSON string as output
func (c Comic) JSON() string {
cJSON, err := json.Marshal(c)
if err != nil {
return ""
}
return string(cJSON)
}
3:设置xkcd客户端以发出请求,解析响应并将其保存到磁盘
xkcd.go在client包内创建文件。

首先定义调用自定义类型ComicNumber作为int

type ComicNumber int
定义常量-

const (
// BaseURL of xkcd
BaseURL string = "https://xkcd.com"
// DefaultClientTimeout is time to wait before cancelling the request
DefaultClientTimeout time.Duration = 30 * time.Second
// LatestComic is the latest comic number according to the xkcd API
LatestComic ComicNumber = 0
)
创建一个struct XKCDClient,它将用于向API发出请求。

// XKCDClient is the client for XKCD
type XKCDClient struct {
client *http.Client
baseURL string
}

// NewXKCDClient creates a new XKCDClient
func NewXKCDClient() *XKCDClient {
return &XKCDClient{
client: &http.Client{
Timeout: DefaultClientTimeout,
},
baseURL: BaseURL,
}
}
将以下4种方法添加到XKCDClient-

SetTimeout()

// SetTimeout overrides the default ClientTimeout
func (hc *XKCDClient) SetTimeout(d time.Duration) {
hc.client.Timeout = d
}
Fetch()

// Fetch retrieves the comic as per provided comic number
func (hc *XKCDClient) Fetch(n ComicNumber, save bool) (model.Comic, error) {
resp, err := hc.client.Get(hc.buildURL(n))
if err != nil {
return model.Comic{}, err
}
defer resp.Body.Close()

var comicResp model.ComicResponse
if err := json.NewDecoder(resp.Body).Decode(&comicResp); err != nil {
    return model.Comic{}, err
}

if save {
    if err := hc.SaveToDisk(comicResp.Img, "."); err != nil {
        fmt.Println("Failed to save image!")
    }
}
return comicResp.Comic(), nil

}
SaveToDisk()

// SaveToDisk downloads and saves the comic locally
func (hc *XKCDClient) SaveToDisk(url, savePath string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()

absSavePath, _ := filepath.Abs(savePath)
filePath := fmt.Sprintf("%s/%s", absSavePath, path.Base(url))

file, err := os.Create(filePath)
if err != nil {
    return err
}
defer file.Close()

_, err = io.Copy(file, resp.Body)
if err != nil {
    return err
}
return nil

}
buildURL()

func (hc *XKCDClient) buildURL(n ComicNumber) string {
var finalURL string
if n == LatestComic {
finalURL = fmt.Sprintf("%s/info.0.json", hc.baseURL)
} else {
finalURL = fmt.Sprintf("%s/%d/info.0.json", hc.baseURL, n)
}
return finalURL
}
4:连接一切
在main()功能内部,我们连接了所有电线-

读取命令参数
实例化 XKCDClient
使用 XKCDClient
输出
读取命令参数-
comicNo := flag.Int(
"n", int(client.LatestComic), "Comic number to fetch (default latest)",
)
clientTimeout := flag.Int64(
"t", int64(client.DefaultClientTimeout.Seconds()), "Client timeout in seconds",
)
saveImage := flag.Bool(
"s", false, "Save image to current directory",
)
outputType := flag.String(
"o", "text", "Print output in format: text/json",
)
flag.Parse()
实例化 XKCDClient
xkcdClient := client.NewXKCDClient()
xkcdClient.SetTimeout(time.Duration(*clientTimeout) * time.Second)
使用 XKCDClient
comic, err := xkcdClient.Fetch(client.ComicNumber(*comicNo), *saveImage)
if err != nil {
log.Println(err)
}
输出
if *outputType == "json" {
fmt.Println(comic.JSON())
} else {
fmt.Println(comic.PrettyString())
}
运行程序,如下所示:

$ go run main.go -n 323 -o json
或将其构建为笔记本电脑的可执行二进制文件,然后运行

$ go build .
$ ./go-grab-xkcd -n 323 -s -o json
在Github存储库中找到完整的源代码-go -grab-xkcd