6ヶ月でwebデザイナーになれる|デジタルハリウッド
6ヶ月でwebデザイナーになれる|デジタルハリウッド
2017.01.13
#7
Go Golang!

【はじめてのGo言語】Go言語でアプリケーションを作ってみよう 〜Webサーバー編〜

Kaz

こんにちは、バックエンドエンジニアの Kaz です。

前回の記事では、Go 言語でアプリケーションを作る第一歩として「 Hello World 」を行いました。今回は Go 言語を使って「 Web サーバー」を作っていきます。

 

Go 言語には標準で「net / http」という HTTP サーバー用のパッケージが含まれていて、これを使うことで、わずか数行のコードだけで非常に簡単に Web サーバーを立ち上げられるようになっています。

 

下準備(ディレクトリの作成)

まず今回の下準備として、ディレクトリだけ作っておきます。

適当に $GOPATH/src/localhost/gohttpserver というディレクトリを作ります。

$ mkdir -p $GOPATH/src/localhost/gohttpserver
$ cd $GOPATH/src/localhost/gohttpserver

 

次に、このディレクトリ内に main.go というファイルを作り、Atom で開きましょう。

$ touch main.go
$ atom main.go

サーバーを作って静的ページを配信する

Atom が開いたら、公式ドキュメントにある「静的ファイルサーバー」のサンプルを参考に、静的なページを配信するサーバーを作ってみます。

使うコードはたったこれだけです。

package main

import (
    "log"
    "net/http"
)

func main() {
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/etc/gohttpserver/doc"))))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

 

このコードは一体何を意味するのでしょう? main 関数に解説を加えてみました

 

func main() {
    // 指定されたパス (/static/) に対してハンドラーを登録します
    http.Handle("/static/",
        // URLパスから接頭辞 (/static/) を取り除くミドルウェア的なハンドラーを生成します
        http.StripPrefix("/static/",
            // URLパスを元に静的ファイルを探して返すハンドラーを生成します
            http.FileServer(
                // 静的ファイルを配置したディレクトリ (/etc/gohttpserver/doc) を読み込みます
                http.Dir("/etc/gohttpserver/doc"),
            ),
        ),
    )
    // ListenAndServe は何かに失敗した時にエラーを返すので、それを記録します。
    // エラーが起きなければ ListenAndServe はブロッキングし続けるので log.Fatal は呼ばれません。
    // ここの log はデフォルトで標準出力に書き出されるので、内容はターミナルにそのまま表示されます
    log.Fatal(
        // 指定されたアドレス (:8080) でHTTPサーバーを起動します
        // 2つめの引数で nil を渡すことで、上の http.Handle で登録したハンドラーが使われるようになります
        http.ListenAndServe(":8080", nil),
    )
}

 

このコードのなかの "/etc/gohttpserver/doc" の部分(以下、静的ファイルディレクトリ)は、適宜書き換えてください。

次に、静的ファイルディレクトリに適当な内容の「index.html」を配置します。

<!DOCTYPE html>
<html>
<body>
    Hello World!
</body>
</html>

 

たったこれだけですが、必要な作業はすべて整いました。

それでは実際にこのサーバーを立ち上げてみましょう。ターミナルを開いて次のコマンドを実行します。

 

go run main.go

 

これで Web サーバーが立ち上がります。Web サーバーを起動しているあいだは処理がブロックされるため、一見ターミナルが動作を停止したように見えるようになりますが、正常な動作なのでご安心ください(停止するには Ctrl+C を押します)。

 

次にブラウザを開いて http://localhost:8080/static/ にアクセスしてみましょう。すると、先ほど保存した index.html が表示されます。これでできあがりです!

 

動的なコンテンツを配信する

次に、動的な情報を持ったページを返す機能を追加してみましょう。

内容を次のように変更して保存します。

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

func clockHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, `
        <!DOCTYPE html>
        <html>
        <body>
            It's %d o'clock now.
        </body>
        </html>
    `, time.Now().Hour())
}

func main() {
    // ハンドラーを登録
    http.HandleFunc("/clock", clockHandler)
    // 先ほどの静的ファイルハンドラー
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/etc/gohttpserver/doc"))))
    // サーバーを起動
    log.Fatal(http.ListenAndServe(":8080", nil))
}

 

保存したら、先ほどと同じく go run main.go を叩いてサーバーを起動します(立ち上げたサーバーは Ctrl+C で終了させてください)

 

次にブラウザを開いて http://localhost:8080/clock にアクセスしてみましょう。そこで clockHandler で書いたように時刻が表示されれば成功です!

これで動的なページを生成するアプリケーションは作れるようになりました。ただしソースコード中に HTML コードを混ぜてしまうのは見た目にも美しくありませんし、規模が大きくなってくるとこの方法だと管理も大変です。

そこで、次は「テンプレート」を使って HTML をソースコードから追い出すことにします。

 

テンプレートを使う

HTML コードをテンプレート化するには、標準パッケージの html/template を利用します。

main.go を下記のように書き換えてください。

package main

import (
    "html/template"
    "log"
    "net/http"
    "time"
)

func clockHandler(w http.ResponseWriter, r *http.Request) {
    // テンプレートをパース
    t := template.Must(template.ParseFiles("/etc/gohttpserver/templates/clock.html.tpl"))

    // テンプレートを描画
    if err := t.ExecuteTemplate(w, "clock.html.tpl", time.Now()); err != nil {
        log.Fatal(err)
    }
}

func main() {
    // ハンドラーを登録
    http.HandleFunc("/clock", clockHandler)
    // 先ほどの静的ファイルハンドラー
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/etc/gohttpserver/doc"))))
    // サーバーを起動
    log.Fatal(http.ListenAndServe(":8080", nil))
}

 

次に /etc/gohttpserver/templates/clock.html.tpl に次の内容を保存します。

<!DOCTYPE html>
<html>
<body>
    It's {{ .Hour }} o'clock now.
</body>
</html>

 

これを保存し、もう一度 go run main.go を叩いてサーバーを起動します。先ほどと同じく http://localhost:8080/clock にアクセスし同じものが表示されていれば完了です!

 

Go 言語では、基本的にこのようにして Web アプリケーションを作っていくことになります。このように、標準パッケージのサーバー (net/http) とテンプレート (html/template) を用いるだけで、静的・動的ページの配信を簡単に実現できてしまいます。

しかし近年の Web アプリケーション開発では、Ajax を使って「最新の情報を動的にロードする」といった使い方も行われるようになりました。これに対応するため、Javascript で扱いやすい「JSON」形式のデータを返す API を作ってみましょう。

 

JSONを返すAPIを作る

Go言語で JSON を扱うには、標準パッケージの encoding/json が利用できます。

main.go を次のように書き換えてみてください。

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "time"
)

func apiClockHandler(w http.ResponseWriter, r *http.Request) {
    // JSONにする構造体
    type ResponseBody struct {
        Time time.Time `json:"time"`
    }
    rb := &ResponseBody{
        Time: time.Now(),
    }

    // ヘッダーをセット
    w.Header().Set("Content-type", "application/json")

    // JSONにエンコードしてレスポンスに書き込む
    if err := json.NewEncoder(w).Encode(rb); err != nil {
        log.Fatal(err)
    }
}

func main() {
    // ハンドラーを登録
    http.HandleFunc("/api/clock", apiClockHandler)
    // サーバーを起動
    log.Fatal(http.ListenAndServe(":8080", nil))
}

 

サーバーの再起動後にブラウザで http://localhost:8080/api/clock にアクセスすると、下記のような結果が返ってくると思います。

{"time":"2016-01-02T03:04:05.678901234+09:00"}

 

あとはこの URL を Javascript で叩くことで、取得したデータを煮るなり焼くなり自由に扱うことができるようになります!

 

さいごに

Go 言語ではこのようにして、標準パッケージを用いるだけで簡単に Web サーバーを立ち上げられるようになっています。

実際の Web アプリケーション開発では、これに加えてデータベースへの接続やデータの取得・保存処理などを行っていくことになります。その詳細なやり方もこの連載内で説明していければと思います!

PHP などで Web サービスを書かれている方も多いかと思いますが、連載第一回目で紹介したように Go 言語は速さなど利点がいっぱいあるので、ぜひこの機会にお試しください。