ボクダイモリ

Life is like a Game

Go言語で簡単なWebサーバを作ってみた

最近Go言語の勉強しているので、書いたコードや学んだことを適当に 書いていきたいと思います。
最近はWebサーバを実装する方法について学んだのでそれについて書いていきます。 開発環境は以下の通り

Goの環境構築はネットにたくさんあると思うので省略します。
Go使ってるうちに便利だなと思ったのが以下のコマンド

cd %GOPATH%

すぐGoの開発環境に移動できるので覚えておくと作業がスムーズになる。

単一のgoファイルでWebサーバを作成

GOPATH内にフォルダを作って、その中にmain.goを作成する。 main.goの中身は以下の通り

package main

import (
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte(`
        <html>
          <head>
            <title>helloworld</title>
          </head>
          <body>
            Hello Go
          </body>
        </html>
      `))
    })
    // Webサーバを開始します
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}

作成したらmain.goのあるディレクトリで以下のコマンドを実行

go run main.go

これでWebサーバが立ち上がります。簡単でいいですね!
ソースコードに書いたとおりのHTMLが返って来ていることを 確認して見てください。 ListenAndServeメソッドではポート8080上でWebサーバを開始しています。
http://localhost:8080/にアクセスすることでhttp.HandleFuncが実行されますので、 ResponseWriter.Writeによって、HTMLが返ってきます。
ここで書いてもらったコードはGoのソースファイルにHTMLをハードコーディングしたものですが、 見た目がちょっと汚いので、次の章でHTML部分をtemplateに分けて作ってみます。

templateを使ったWebサーバ

前の章でmain.goを作成したフォルダにtemplatesという名前でフォルダを作って そのフォルダにhelloworld.htmlを作成します。
helloworld.htmlの中身は前の章で書いたHTML部分を持ってきます。

<html>
  <head>
    <title>helloworld</title>
  </head>
  <body>
    Hello Go templateにしました
  </body>
</html>

先ほど作成したWebサーバとの違いが分かるように内容をちょっと変えています。
このtemplateを呼び出すようにmain.goを以下のように修正

package main

import (
    "log"
    "net/http"
    "path/filepath"
    "sync"
    "text/template"
)

// templは1つのテンプレートを表します
type templateHandler struct {
    once     sync.Once
    filename string
    templ    *template.Template
}

// ServerHTTPはHTTPリクエストを処理します
func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    t.once.Do(func() {
        t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
    })
    t.templ.Execute(w, nil)
}
func main() {
    //ルート
    http.Handle("/", &templateHandler{filename: "helloworld.html"})
    //Webサーバを開始します
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}

上記のように編集してからサーバを再起動すると、templatesの中に作った HTMLファイルが表示されているかと思います。
テンプレートの読み込み部分ですが、sync.Once型のDoメソッド内部でテンプレートのファイル名が渡されています。
そしてsync.Once型のDoメソッドによりテンプレートは呼び出された時に1回だけコンパイルされます。(遅延初期化) なので、サーバ起動時にtemplatesフォルダがなかったりテンプレートファイルがなかったりしても動作します。
ただ、ないテンプレートを呼び出そうとすると動かなくなりますが。 また、コンパイルは呼び出し時に1回だけなのでテンプレートを修正した後はサーバを再起動しない限り 反映されません。

まとめ

  • Webサーバはmain.goとtemplatesフォルダという構成になっている
  • templates内にはHTMLをテンプレートファイルとして配置
  • http.ListenAndServeでWebサーバを立ち上げる
  • sync.once.Doで遅延初期化を実現できる

参考にしている本

Go言語によるWebアプリケーション開発

Go言語によるWebアプリケーション開発

今後も勉強を進めていくので、定期的にブログ更新していきます。