Go言語で 標準package のみを使用して、
httpサーバーを作成するのをやってみたいと思います。
ただ httpサーバー を立てるだけだと
すぐできてしまいそうなので、今回は go:embed を使ってみたいと思います。
go:embed とは
httpサーバーを立てて何かページを返すようなものを作ろうと思っています。
あとリソースは、外部アクセスさせずにローカルで完結するようにします。
そのためには、リソースをローカルで持たなければいけません。
例えば、画像・CSS・JavaScript などのファイルとか。。。
実行体(exe)と同じところにファイルを置いて、
exeから読み込んで返してもらってもよいのですが
ファイルを入れ忘れるとかがあると面倒なので・・・ワンバイナリにしたい!
そんな時に使えるのが go:embed です。
リソースも実行体に含めてワンバイナリにしてくれます。
Go 1.16 からリリースされたので、それ以上で使えます!
使い方もそんなに難しくないので、さっそくやってみます。
httpサーバーを立てる
まずは、httpサーバーを作ります。
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", indexPage)
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal(err)
}
}
func indexPage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World.")
}
あとは go build して 出来上がったexe をコマンドプロンプトから実行する。
※最初はgo modも作成してください
実行したあとに、ブラウザで http://localhost:3000 にアクセスしてみましょう。
文字が表示されました!
ちなみに実行を止めるときは、
コマンドプロンプト上で Ctrl + C を押すと実行を停止できます。
ページを作成する
ちょっと もの寂しいので、ページを作って返してみます。
Bootstrap4 でなんとなく作ります。
Compiled CSS and JS の Download からファイルを一式(zip)落としてきます。
あと jQuery も必要なので、落としてきます。
Downloading jQuery の Download the compressed, production jQuery 3.6.0 あたりからファイル(js)を落としてきます。
落としたファイル類は、解凍して ローカルの./static に入れます。
あと./templates に ページのテンプレート を作って入れます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ .Title }}</title>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<script src="/static/js/jquery-3.6.0.min.js"></script>
<script src="/static/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container mt-2">
<div class="card">
<div class="card-header">
Profile
</div>
<div class="card-body">
<h5 class="card-title">hiro</h5>
<p class="card-text">
見習いエンジニア Go / Perl など勉強中
</p>
</div>
</div>
</div>
</body>
</html>
先ほど作成したGoのソースコードをベースに少し変更します。
テンプレートを読み取って、ページを返すようにします。
あとリソースもローカルから返すように変更します。
package main
import (
"log"
"net/http"
"text/template"
)
func main() {
http.HandleFunc("/", indexPage)
// ローカルからファイルを返す
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal(err)
}
}
func indexPage(w http.ResponseWriter, r *http.Request) {
// テンプレートを読み取ってページを作成する
t, err := template.ParseFiles("templates/index.tmpl")
if err != nil {
log.Fatalln(err)
}
err = t.Execute(w, struct {
Title string
}{
Title: "プロフィール",
})
if err != nil {
log.Println(err)
}
}
最終的にはこんな構成になるはず。
./
│ go.mod
│ server.go
│
├─static
│ ├─css
│ │ bootstrap-grid.css
│ │ bootstrap-grid.css.map
│ │ bootstrap-grid.min.css
│ │ bootstrap-grid.min.css.map
│ │ bootstrap-reboot.css
│ │ bootstrap-reboot.css.map
│ │ bootstrap-reboot.min.css
│ │ bootstrap-reboot.min.css.map
│ │ bootstrap.css
│ │ bootstrap.css.map
│ │ bootstrap.min.css
│ │ bootstrap.min.css.map
│ │
│ └─js
│ bootstrap.bundle.js
│ bootstrap.bundle.js.map
│ bootstrap.bundle.min.js
│ bootstrap.bundle.min.js.map
│ bootstrap.js
│ bootstrap.js.map
│ bootstrap.min.js
│ bootstrap.min.js.map
│ jquery-3.6.0.min.js
│
└─templates
index.tmpl
go build して アクセスしてみましょう。
ページが表示できました!
この状態でも、
exeファイル と staticフォルダ と templatesフォルダ を持っていけば動くのですが
go:embedでワンバイナリにしてみましょう。
go:embed でファイルを埋め込む
go:embed で指定してあげて、少し変更するだけ 簡単に使用できます!
package main
import (
"embed"
"log"
"net/http"
"text/template"
)
//go:embed static
var static embed.FS
//go:embed templates
var templates embed.FS
func main() {
http.HandleFunc("/", indexPage)
// http.FS に変更するだけ
http.Handle("/static/", http.FileServer(http.FS(static)))
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal(err)
}
}
func indexPage(w http.ResponseWriter, r *http.Request) {
// ParseFS に変更するだけ
t, err := template.ParseFS(templates, "templates/index.tmpl")
if err != nil {
log.Fatalln(err)
}
err = t.Execute(w, struct {
Title string
}{
Title: "プロフィール",
})
if err != nil {
log.Println(err)
}
}
//go:embed <ディレクトリ or ファイル>で指定してあげる。
※//の後ろは半角スペースあけない!
そしてその直下に変数を定義する。
これだけでFileSystemな変数ができちゃいます。
あとは go buildすれば、ワンバイナリになるので
リソースとなるファイル類がなくても、実行体だけを持って行って実行するだけ。
テンプレート側はソースコード内で指定する必要がありますが、
static側はファイルを置いて、ページから読み込むだけという感じで使えますね!
まとめ
今回初めて go:embed を使用してみましたが、
簡単に使えて便利です!
今回紹介したもの以外にも
いろいろな機会に使用できると思うので、みなさん使ってみてくださいね!

