問題
Go言語でサーバーを立ち上げてHTMLを表示させる際、cssとjsファイルをHTMLに適用させるのに少し時間がかかりました。
備忘録としてHTML + CSS + JavaScriptのページを表示する方法を記載していきます。
解決方法
結論から言うと、go:embedを使用しました。
下記のテックブログを参考にして、静的ファイルを変数に格納 -> ファイルシステムを取得して読み込むという方法で課題を解決しました。
下記にコードを記載しますが、全てを記載すると記述が多くなるため省略してあります。
全コードが気になる方はgitを確認してください。
https://github.com/Makoto87/test_go_embed
goのバージョン
go version go1.17.3 darwin/amd64
ディレクトリ構成
.
├── go.mod
├── main.go
└── views
├── main.html
├── scripts
│ └── textarea.js
└── styles
└── stylesheet.css
goファイル
package main
import (
"embed"
"html/template"
"net/http"
)
// viewsディレクトリ下のファイルを全て変数に格納する
//go:embed views/*
var views embed.FS
// メイン画面の表示
var templates = template.Must(template.ParseFS(views, "views/main.html"))
func viewHandler(w http.ResponseWriter, r *http.Request) {
if err := templates.ExecuteTemplate(w, "main.html", nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func main() {
http.HandleFunc("/view/", viewHandler)
// viewsに格納したファイルを全て公開
http.Handle("/views/", http.FileServer(http.FS(views)))
// httpサーバーを立ち上げ
http.ListenAndServe(":8080", nil)
}
htmlファイル
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 前略 -->
<!-- go:embedで読み込んだviewsディレクトリ下のcssを読込 -->
<link rel="stylesheet" href="/views/styles/stylesheet.css">
</head>
<body>
<!-- 前略 -->
<!-- go:embedで読み込んだviewsディレクトリ下のjsファイルを読込 -->
<script src="/views/scripts/textarea.js"></script>
</body>
</html>
書いたコードで表示された画面
ちゃんとCSSが適用されています
CSSではtextareaの高さを指定しました
JavaScriptも適用されています
入力した文字に合わせてtextareaが変化するようになっています
解説
embed
をインポートし、変数の上に //go:embed views/*
という記述をすれば、viewsディレクトリ下のファイルを変数に読み込んでくれます。
embedパッケージについて公式ドキュメントではこう記載されています。
Package embed provides access to files embedded in the running Go program.
Go source files that import "embed" can use the //go:embed directive to initialize a variable of type string, []byte, or FS with the contents of files read from the package directory or subdirectories at compile time.
簡単に訳すと、
embedを使用することで、コンパイル時にパッケージディレクトリまたはサブディレクトリから読み取られたファイルの内容で変数を初期化できる。
という感じの内容になっています。
ファイルを変数に格納した後は、ファイルを読み込めるようにする処理を記述します。それが下記のコードになります
http.Handle("/views/", http.FileServer(http.FS(views)))
このコードによって、
http.FSでviews
をhttp.FileSystemに変換し、
http.FileServerでファイルシステム(views)の内容をHTTPリクエストに返すhandlerを返し、
http.Handleで、httpサーバーが立ち上がった後に、/views/
というパスを指定されたらファイルシステムが返されるようになります。
ただしhttp.Handleを使うときは、http.ListenAndServeの第二引数をnilにしておく必要があります。
パスを指定してファイルシステムが返されるようになれば、htmlファイル上でそのパスを活用して、cssファイルとjsファイルを読み込んだHTMLを表示できます。
cssを読み込む場合は下記の記述になります。
<link rel="stylesheet" href="/views/styles/stylesheet.css">
embedで読み込んだファイルはバイナリにも埋め込まれるというメリットがあります。
これによって、ビルド時のディレクトリ構成とは異なる構成になったとしても、影響を受けずに正常に静的ファイルを返すことができます。