紙芝居用の簡易サーバを書く【Go 1.16 版】

ずいぶん前に

という記事を書いたが,これで作ったコードは任意のディレクトリをドキュメント・ルートにできるという結構ヤバい代物である。

ところで Go 1.16 リリースで真っ先に思ったのは「これで安全に紙芝居ができるぢゃん」だった。 Go 1.16 で追加された embed および io/fs 標準パッケージを使えば,サードパーティの外部パッケージを使わずとも,簡単に「紙芝居」が作れる。

さっそく試してみよう。 こんな感じかな。

package main

import (
    "embed"
    "fmt"
    "net/http"
    "os"
)

//go:embed html
var assets embed.FS

func main() {
    addr := "localhost:3000"
    fmt.Printf("Open http://%s/\n", addr)
    fmt.Println("Press ctrl+c to stop")

    http.Handle("/", http.FileServer(http.FS(assets)))
    if err := http.ListenAndServe(addr, nil); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
}

ここで //go:embed ディレクティブがコンパイラに html の埋め込みを指示している部分である。 html は実在のディレクトリで,以下の内容の index.html ファイルを格納している1

<!DOCTYPE html>
<html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        <p>Hello World!</p>
    </body>
</html>

embed.FS 型のインスタンスはそのままでは http.FileSystem 型として使えないので http.FS() 関数で http.FileSystem 型に適合するよう変換している。

説明はこのくらいにして,とりあえず動かしてみよう。

$ go run sample1.go 
Open http://localhost:3000/
Press ctrl+c to stop

これでブラウザで http://localhost:3000/ にアクセスしてみると。

localhost:3000/

ありゃりゃーん。 ちょっと切ない感じになってしまった。

html ディレクトリをドキュメント・ルートに出来ないかな,と思って色々眺めてたら fs.Sub() 関数が使えるっぽい。

というわけで,さきほどのコードを少し書き換える。

package main

import (
    "embed"
    "fmt"
    "io/fs"
    "net/http"
    "os"
)

//go:embed html
var assets embed.FS

func main() {
    addr := "localhost:3000"
    fmt.Printf("Open http://%s/\n", addr)
    fmt.Println("Press ctrl+c to stop")

    root, _ := fs.Sub(assets, "html")
    http.Handle("/", http.FileServer(http.FS(root)))
    if err := http.ListenAndServe(addr, nil); err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
}

エラー処理をサボっているがご容赦。 では,このコードを起動してみる。

$ go run sample2.go 
Open http://localhost:3000/
Press ctrl+c to stop

ブラウザでアクセスしてみると。

Hello

よーし,うむうむ,よーし。 これで安全に紙芝居ができるな。 go build でシングル・バイナリにまとめれば持ち運びもOK(笑)

//go:embed ディレクティブはかなり便利に使えるようで

package main

import (
    _ "embed"
    "fmt"
)

//go:embed html/index.html
var hello string

func main() {
    fmt.Println(hello)
}

てな感じに,ファイル内容を []byte または string 型のデータに展開してくれる。

$ go run sample3.go 
<!DOCTYPE html>
<html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        <p>Hello World!</p>
    </body>
</html>

こりゃあ,ひょっとしてテストが捗るねぇ。 データの読み込みを //go:embed ディレクティブで簡略化できるもんね。

ブックマーク

参考図書

photo
プログラミング言語Go
アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)
丸善出版 2016-06-20 (Release 2021-07-13)
Kindle版
B099928SJD (ASIN)
評価     

Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想はこちら

reviewed by Spiegel on 2021-05-22 (powered by PA-APIv5)


  1. //go:embed ディレクティブはあくまでもファイルに対してディレクトリ構造ごと埋め込むものなので,空のディレクトリは埋め込めない。 ↩︎