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

  1. 紙芝居用の簡易 Web サーバを Go 言語で書く ←イマココ
  2. 紙芝居用の簡易サーバを書く【Go 1.16 版】 ←イマココ
  3. 紙芝居用の簡易 Web サーバを Go 言語で書く【叱られ編】

ずいぶん前に「紙芝居用の簡易 Web サーバを Go 言語で書く」という記事を書いたが,これで作ったコードは任意のディレクトリをドキュメント・ルートにできるという結構ヤバい代物である。

ところで 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 (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)
丸善出版 2016-06-20
単行本(ソフトカバー)
4621300253 (ASIN), 9784621300251 (EAN), 4621300253 (ISBN), 9784621300251 (ISBN)
評価     

著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。この本は Go 言語の教科書と言ってもいいだろう。

reviewed by Spiegel on 2016-07-13 (powered by PA-APIv5)


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