Go言語のWebフレームワークGinと、デスクトップアプリ開発用ライブラリのLorcaを使って簡易的なニュースビューワーを作ってみました。
GitHubにコードをアップしています(https://github.com/wassan128/newsman/)。
あらまし
ニュースを見る癖が無く、世間に置いて行かれがちな自分のためにニュースビューワーを作ろうと思ったのがきっかけです。Node.js(Electron)で書こうとも思ったのですが、せっかく最近Go言語を勉強し始めたのでGoで似たようなことができないかと調べていたところ、同じようなことができるものがあることを知ったのでやってみた次第です。
作るに当たって
- ニュースを何かAPIで取得
- 簡易的なWebアプリとして起動
- デスクトップアプリ風ウィンドウに描画する
ということをします。一旦完成とした画面は以下のようになっています。
環境
- Ubuntu18.04 1 LTS 64bit
- Go言語バージョン
go1.10.4 linux/amd64
Gin
Go製のWebフレームワークです(https://github.com/gin-gonic/gin)。
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
と、イケてるDescriptionもさることながら、初学者の私でもある程度直感に従って使えるフレームワークであるように感じたので採用しました(他に良いものがあれば知りたいです)。
Lorca
Go製のデスクトップアプリ開発用ライブラリです(https://github.com/zserge/lorca)。HTML5でUIを書くことができる、軽量なライブラリとなっており、Go言語コード内でJavaScriptコードを呼び出せるなどの特徴があるようです(今回のプログラムではその機能は使っていませんが)。
開発①ニュースの取得
今回ニュース取得のAPIにはNews API(https://newsapi.org/)のTop headlinesを用いました。
News APIにデベロッパー登録するとAPIキーがもらえます(手順は割愛します)。リクエストは
https://newsapi.org/v2/top-headlines?country=jp&apiKey=XXX
に飛ばします。今回は日本のニュースがほしいのでcountry
はjp
としています。
APIからはJSONが返されるので、これをGo言語側で受け取ります。Top headlinesのサンプルレスポンスを見るとNews APIのJSONは
{
"status": "ok",
"totalResults": 36,
"articles": [
{
"source": {
"id": "cnn",
"name": "CNN"
},
"author": null,
"title": "Mueller starts to piece together Russia puzzle in most significant move yet - CNN",
"description": null,
"url": "https://www.cnn.com/2018/11/30/politics/donald-trump-michael-cohen-robert-mueller/index.html",
"urlToImage": null,
"publishedAt": "2018-11-30T06:53:00Z",
"content": null
}, ...
という構造になっているので、これをそのまま
type Source struct {
Id string `json:"id"`
Name string `json:"name"`
}
type Article struct {
Source Source `json:"source"`
Author string `json:"author"`
Title string `json:"title"`
Description string `json:"description"`
Url string `json:"url"`
UrlToImage string `json:"urlToImage"`
PublishedAt string `json:"publishedAt"`
Content string `json:"content"`
}
type News struct {
Status string `json:"status"`
TotalResults int `json:"totalResults"`
Articles []Article `json:"articles"`
}
と構造体に落とし込みます。これで
// ニュースをHTTPリクエストし
res, err := http.Get(EndPoint)
if err != nil {
fmt.Println("json load error")
}
defer res.Body.Close()
// レスポンスのBodyからJSON形式のニュースを取り出し
var news News
data, _ := ioutil.ReadAll(res.Body)
// JSONをデコードしてNews型変数にデータを入れる
if err := json.Unmarshal(data, &news); err != nil {
fmt.Println(err)
}
とすると変数news
にニュースの情報を格納することができます。このJSONと構造体の対応付けなどはGo言語ならでは感があるなと思いました(初学者並感)。
開発②Webアプリ化
「/」にアクセスするとニュースのタイトルが出るような実装にしたいので、
func server(news News) {
router := gin.Default()
// cssや画像は「static」ディレクトリ配下に設置することとする
router.Static("/static", filepath.Join(os.Getenv("GOPATH"), repo_path, "static"))
// テンプレートファイルは「templates」ディレクトリ配下に設置することとする
router.LoadHTMLGlob(filepath.Join(os.Getenv("GOPATH"), repo_path, "templates/*"))
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"articles": news.Articles,
})
})
go router.Run()
}
というような関数を用意します。router.Run()
でポートを指定できますが今回は省略しているので、デフォルトの「:8080
」になります。
index.tmpl
というテンプレートファイルにarticles
という名前でニュースの記事データを渡しています。ここで渡した値はテンプレートファイル内で
{{range .article}}
<p>{{.Title}}</p>
{{end}}
というように参照できます。
router.Run()
先頭にgo
をつけてgoroutineにしているのは次ステップのLorcaでの画面描画をブロックしないためです。
開発③デスクトップアプリにする
ここまででニュースを取得して表示するWebアプリができているので、あとはこれをLorcaの力でデスクトップアプリにするだけです。
var ui lorca.UI
// 幅320、高さ480のウィンドウを生成
ui, _ = lorca.New("", "", 320, 480)
defer ui.Close()
// Ginで動かしたWebアプリのURLを指定
ui.Load("http://localhost:8080")
<-ui.Done()
とても簡単にできました。
プログラム実行の時系列としては「ニュースの取得」→「サーバー起動」→「デスクトップアプリにする」なので、ループに入るサーバー起動部を非同期処理にすることでサーバーを裏で動かしつつそこでホスティングされているコンテンツを描画しています。
まとめ
思っていたよりも簡単にデスクトップアプリ化することができました。まだまだ改良の余地もありそうですし、これからもちょっとずつ機能追加や他のアプリも作ってみたいです。
Go言語を学び始めて日が浅く、間違いやここはもっとこうした方が良いよなどあればぜひアドバイスいただきたいので、よろしくお願いします。