Go言語によるWebサーバー作成入門 ーテンプレートー
(作成2020.10.21)
まずはテキスト出力
main.goという名のファイルを作成し、次の文をペーストしてください。
package main
import "fmt"
func main() {
message := "Hello, World!"
tpl := `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>` + message + `</h1>
</body>
</html>
`
fmt.Println(tpl)
}
これを実行して、次のように出力されれば正解です。
ただ、tplに代入した文字列を表示しただけです。
htmlファイルを出力する
次の文を実行すると、index.htmlファイルが作成されます。
package main
import (
"fmt"
"io"
"log"
"os"
"strings"
)
func main() {
message := "Hello, World!"
str := fmt.Sprint(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>` + message + `</h1>
</body>
</html>
`)
nf, err := os.Create("index.html")
if err != nil {
log.Fatal("error creating file", err)
}
defer nf.Close()
io.Copy(nf, strings.NewReader(str))
}
os.Createで空ファイルを作成します。最終的にクローズが必要なのでdeferで命令を送っておきます。io.Copyをつかって内容をコピーしますが、コピー元はstrings.NewReaderを使ってインターフェイスにしてつなぎます。この辺はじきに慣れてくると思うので、悩まないでこういうもんだとこのまま覚えましょう。
さぁ、これでWebページが完成しました!、、とは言っても、コードをべた書きしただけなので、これなら最初からHTMLファイルを作っとけよ!という話で、全然ありがたみがないですね。なので次はテンプレートを作成し、後から文字を追加できるようにします。
入力する文字を後から指示する
次のコードを「go run main.go Hello!」と実行してみてください。
package main
import (
"fmt"
"io"
"log"
"os"
"strings"
)
func main() {
message := os.Args[1]
fmt.Println(os.Args[0])
fmt.Println(os.Args[1])
str := fmt.Sprint(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>` + message + `</h1>
</body>
</html>
`)
nf, err := os.Create("index.html")
if err != nil {
log.Fatal("error creating file", err)
}
defer nf.Close()
io.Copy(nf, strings.NewReader(str))
}
/* ターミナルでは次の文で実行して:
go run main.go Hello!
*/
まぁ確かに、、ね。という声が聞こえてきそう。。。一応、引数の使い方でした。これもひとつのテンプレート。
外部ファイルをテンプレートとして呼び出す
テンプレートファイルとしてindex.gohtmlを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
そしてGoファイルを作成します。
package main
import (
"log"
"os"
"text/template"
)
func main() {
tpl, err := template.ParseFiles("tpl.gohtml")
if err != nil {
log.Fatalln(err)
}
err = tpl.Execute(os.Stdout, nil)
if err != nil {
log.Fatalln(err)
}
}
これを実行すると、ターミナル上にテンプレートファイルの中身が出力されます。templateパッケージを読み込んでParseFilesでテンプレートファイルをtplに格納して、tpl.Executeで標準出力(画面)に出力しています。でもやっぱりこれでは面白くない、ちゃんとファイルを作成しましょう。
テンプレートを使ってファイルを出力する
package main
import (
"log"
"os"
"text/template"
)
func main() {
tpl, err := template.ParseFiles("tpl.gohtml")
if err != nil {
log.Fatalln(err)
}
nf, err := os.Create("index.html")
if err != nil {
log.Fatalln("error creating file", err)
}
defer nf.Close()
err = tpl.Execute(nf, nil)
if err != nil {
log.Fatalln(err)
}
}
これでテンプレートファイルの中身がindex.htmlとして出力されます。ファイルのクローズを忘れずに。前の項で使ったtpl.Executeの第1引数に空ファイルを指定して書き込みます。
しかしまだ、あまりありがたみがないコードですね。
複数のテンプレートを扱う ParseFiles
まず、テンプレートを3つほど作成します。
いーち、
にー、
さんっ、だー!
そしてコードはこちら。
package main
import (
"log"
"os"
"text/template"
)
func main() {
tpl, _ := template.ParseFiles("one.txt")
_ = tpl.Execute(os.Stdout, nil)
tpl, _ = tpl.ParseFiles("two.txt", "three.txt")
_ = tpl.ExecuteTemplate(os.Stdout, "two.txt", nil)
_ = tpl.ExecuteTemplate(os.Stdout, "three.txt", nil)
_ = tpl.ExecuteTemplate(os.Stdout, "one.txt", nil)
_ = tpl.Execute(os.Stdout, nil)
}
エラーハンドリングはコードが長くなって読みにくくなるので省略しました。ParseFilesでテンプレートを見込むのはこれまでと同じ。2つのテンプレートを同時に読み込むこともできます。連続してParseFilesを呼び出すとテンプレートは追加されます。そして、tpl.ExecuteTemplateでテンプレートを呼び出します。第2引数で呼び出すテンプレートを指定しています。ExecuteはParseFilesに格納したテンプレートのうちの一番最初が呼び出されます。
複数のテンプレートを扱う ParseGlob
テンプレートを1ファイルずつ指定するのは面倒ですよね。*が使えるParseGlobというのがあります。ここでは先ほどのテンプレートファイルをtemplateディレクトリに移動して実行します。
package main
import (
"log"
"os"
"text/template"
)
func main() {
tpl, _ := template.ParseGlob("templates/*")
_ = tpl.Execute(os.Stdout, nil)
_ = tpl.ExecuteTemplate(os.Stdout, "two.txt", nil)
_ = tpl.ExecuteTemplate(os.Stdout, "three.txt", nil)
}
コードをちょっとだけカッコつける
package main
import (
"log"
"os"
"text/template"
)
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseGlob("templates/*"))
}
func main() {
_ = tpl.Execute(os.Stdout, nil)
_ = tpl.ExecuteTemplate(os.Stdout, "two.txt", nil)
_ = tpl.ExecuteTemplate(os.Stdout, "three.txt", nil)
}
func init()でmain()の先にテンプレートを格納して少しカッコつけてます。また、ParseGlobにMustを呼び出しています。こうするとエラーハンドリングを中で処理してくれるのでコードを書かなくて済みます。