Home >Backend Development >Golang >Build a CLI program using Go
You may have practiced Go syntax again and again, but unless you build an application yourself, you have not experienced the real feel of writing applications in Go. .
In this blog post, we will build a CLI application in Go, let’s call it go-grab-xkcd. The application pulls comics from XKCD and provides them for you through command line parameters Provide various operation options.
We will build the entire application using only the Go standard library without using external dependencies.
The idea of this application may seem a bit silly, but the purpose is Practice writing production-level (sort of) code in Go without trying to get acquired by Google.
There’s a bonus at the end
Note: This article assumes that the reader is already familiar with Go syntax and terminology, and Somewhere between beginner and intermediate.
Let's run the app first and then move on
$ 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" }
You can try other options by downloading and running the app on your computer .
After the end of this tutorial you will be familiar with the following topics:
Receiving command line parameters
Converting between JSON and Go structures
Make API calls
Create files (download and save from the network)
String operations
The following is the project structure
$ tree go-grab-xkcd go-grab-xkcd ├── client │ └── xkcd.go └── model └── comic.go ├── main.go └── go.mod go.mod - Go Modules file used in Go for package management main.go - Main entrypoint of the application comic.go - Go representation of the data as a struct and operations on it xkcd.go - xkcd client for making HTTP calls to the API, parsing response and saving to disk
1: Initialize project
Create a go.mod file -
$ go mod init
This will help you with package management (think package.json files in JS).
2: xkcd API
xkcd is amazing and you don’t need any registration or access keys to use their API. Open the xkcd API "Documentation" and you will find that there are two endpoints -
http://xkcd.com/info.0.json - Get the latest comics
http://xkcd. com/614/info.0.json - Get the specified comic by comic number
The following is the JSON response of these endpoints-
{ "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": "" }
Article related xkcd
2: Comic creation model
According to the above JSON response, we create a struct called ComicResponse in comic.go in the model package
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"` }
You can use the JSON-to-Go tool to automatically generate it from JSON Structure.
By the way create another structure that will be used to output data from our application.
type Comic struct { Title string `json:"title"` Number int `json:"number"` Date string `json:"date"` Description string `json:"description"` Image string `json:"image"` }
Add the following two methods to the ComicResponse structure
// FormattedDate 函数将格式化日期元素为一个字符串 func (cr ComicResponse) FormattedDate() string { return fmt.Sprintf("%s-%s-%s", cr.Day, cr.Month, cr.Year) } // Comic 函数将从 API 接收到的 ComicResponse 转换为应用程序的输出格式, Comic 结构体 func (cr ComicResponse) Comic() Comic { return Comic{ Title: cr.Title, Number: cr.Num, Date: cr.FormattedDate(), Description: cr.Alt, Image: cr.Img, } }
Then add the following two methods to the Comic structure
// PrettyString 函数创建一个漂亮的 Comic 字符串并用于输出 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 函数将 Comic 结构体转换为 JSON, 我们将使用 JSON 字符串作为输出内容 func (c Comic) JSON() string { cJSON, err := json.Marshal(c) if err != nil { return "" } return string(cJSON) }
3: Set up the xkcd client to initiate a request, parse the response and save it to disk
Create xkcd in the client package. go file.
First define a custom type called ComicNumber, the data type is int
type ComicNumber int
Define constants
const ( // xkcd 的 BaseURL BaseURL string = "https://xkcd.com" // DefaultClientTimeout 是取消请求之前要等待的时间 DefaultClientTimeout time.Duration = 30 * time.Second // 根据 xkcd API, LatestComic 是最新的漫画编号 LatestComic ComicNumber = 0 )
Create a structure XKCDClient, which will be used to make requests to the API.
// XKCDClient 是 XKCD 的客户端结构体 type XKCDClient struct { client *http.Client baseURL string } // NewXKCDClient 创建一个新的 XKCDClient func NewXKCDClient() *XKCDClient { return &XKCDClient{ client: &http.Client{ Timeout: DefaultClientTimeout, }, baseURL: BaseURL, } }
Add the following 4 methods to XKCDClient
SetTimeout()
// SetTimeout 重写了默认的 ClientTimeout func (hc *XKCDClient) SetTimeout(d time.Duration) { hc.client.Timeout = d }
Fetch()
// Fetch 根据提供的漫画编号检索漫画 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 下载并保存漫画到本地磁盘 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: Connect all
Inside the main() function we link all the content
read Command line parameters
Instantiate XKCDClient
Use XKCDClient to pull data from the API
Output
Read command line parameters
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()
Instantiate XKCDClient
xkcdClient := client.NewXKCDClient() xkcdClient.SetTimeout(time.Duration(*clientTimeout) * time.Second)
Use XKCDClient to pull data from API
comic, err := xkcdClient.Fetch(client.ComicNumber(*comicNo), *saveImage) if err != nil { log.Println(err) }
Output
if *outputType == "json" { fmt.Println(comic.JSON()) } else { fmt.Println(comic.PrettyString()) }
The program runs as follows
$ go run main.go -n 323 -o json
Or build it as a binary executable on your laptop and run it
$ go build . $ ./go-grab-xkcd -n 323 -s -o json
The complete source code can be found in this Github repository - go-grab-xkcd
Extra bonus
You can download multiple comics in sequence by using this simple shell magic tool
$ for i in {1..10}; do ./go-grab-xkcd -n $i -s; done;
The above shell code simply calls go-grab-xkcd in a for loop command, since xkcd uses sequence integers, replace the i value with the comic number as the comic number/ID.
Recommended tutorials: "PHP" "Go Tutorial"
The above is the detailed content of Build a CLI program using Go. For more information, please follow other related articles on the PHP Chinese website!