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

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

Kaz

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 言語は速さなど利点がいっぱいあるので、ぜひこの機会にお試しください。

 

LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。

Webサイト制作の実績・料金を見る

この記事のシェア数

リードエンジニアのKazです。 LIGで担当している仕事は自社サービスや受託案件のバックエンド開発。知識を極めるために英語を欲して勢いでオーストラリアに渡航、現地のWeb制作会社で開発者として働き、その後東京に戻り外資系金融サイトで運営と開発を担ったりしていました。今では台東区から日本を見据えてサービスを開発しております、と言いたいところですがまだまだ広い世界を知りきらぬペーペーです。どうぞよろしくお願いします。

このメンバーの記事をもっと読む
Go Golang! | 7 articles