SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

EclipseでGoプログラミング! GoClipseのインストールとGojiフレームワークを使ったWeb APIの作成

  • X ポスト
  • このエントリーをはてなブックマークに追加

GojiによるWebアプリケーションの作成

 ここからはいよいよ、Gojiフレームワーク(以下、Goji)を使ってWeb APIを作成していきます。

net/http

 Gojiについて解説する前に、Goの標準パッケージである「net/http」に触れておきましょう。Gojiは net/http パッケージに対する薄いラッパーとして構成されています。そのため、net/http パッケージについての知識も必要になるのです。

 次のコードは、net/http パッケージだけを使用して作成した最小限のWebアプリケーションです。

package main

import (
    "io"
    "net/http"
)

func handler(w http.ResponseWriter, req *http.Request) {
    io.WriteString(w, "Hello, World!¥n")
}

func main() {
    http.HandleFunc("/hello", handler)
    http.ListenAndServe(":8080", nil)
}

 

 Eclipseで実行させてWebサーバが起動したら、Webブラウザから「http://localhost:8080/hello」へアクセスしてみます。「Hello, World!」とWebブラウザ上に表示されれば成功です。

 このようにGoは、本体にWebアプリケーションを作成するための枠組みを備えています。Gojiは net/http パッケージが提供するHTTPサーバ機能の基本実装へ、有用な機能(柔軟なルーティング、ロギング、ミドルウェアなど)を追加するという仕組みで構築されています。

Gojiの導入

 Gojiのインストールには、Gocode同様に go get コマンドを利用します。

C:¥> go get -u github.com/zenazn/goji

 

 go get コマンドの実行に成功したら、Eclipseのプロジェクト上から環境変数 GOPATH に含まれるパッケージの内容を確認してみます。

GOPATH内のgoji
GOPATH内のgoji

 

 環境変数 GOPATH に、goji パッケージが含まれていることが確認できました。それではさっそく使ってみましょう。まずは、下記のサンプルコードを動かしてみます。

package main

import (
    "fmt"
    "net/http"

    "github.com/zenazn/goji"
    "github.com/zenazn/goji/web"
)

// 「Hello, World!」を出力するハンドラ
func hello(c web.C, w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", c.URLParams["name"])
}

func main() {
    // ルーティングの定義とサーバの起動
    goji.Get("/hello/:name", hello)
    goji.Serve()
}

 

 コードを保存してEclipseで実行します。net/http パッケージ使用バージョンの動作とは異なり、コンソールにログが表示されます。Webブラウザを開いて「http://localhost:8000/hello/goji」にアクセスしてみましょう。

Hello, goji!
Hello, goji!

 

 「/hello/:name」というルーティングの「:name」部分をパラメータとして取り出して表示に反映させていることが分かります。また、Eclipseのコンソールには次のように、アクセスログとその処理ステータスが出力されています。

gojiのログ出力
gojiのログ出力

 

 ここから少しコードの詳細を追います。まずはWebアプリケーションの処理の中心となるハンドラの定義についてです。

func handler(c web.C, w http.ResponseWriter, r *http.Request) {
    // http.Requestはリクエスト情報を保持する
    fmt.Fprintf(w, "URL=%s, Host=%s, Method=%s, RemoteAddr=%s", r.URL, r.Host, r.Method, r.RemoteAddr)
    // web.Cを使用してURLのパラメータを取り出せる
    fmt.Fprintf(w, "id=%s, name=%s", c.URLParams["id"], c.URLParams["name"])
}

 

 net/httpで定義するハンドラは http.ResponseWriter 型と *http.Request 型の2つの引数をとりますが、Gojiのハンドラには web.C 型の引数が追加されています。この構造体により、URLに含まれるパラメータを簡単に取り出すことができます。

 このハンドラが行うべきことは、http.ResponseWriter へのレスポンス出力です。上記のコード例では、Content-Type が "text/plain" であるテキストデータを出力しています。単純なHTMLを出力するのであれば、Goのヒアドキュメントを使って次のように書くこともできます。

func handler(c web.C, w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, `<!DOCTYPE html>
<html lang="ja">
<head></head>
<body>
<h1>タイトル</h1>
</body>
</html>`)
}

 

 お気づきかもしれませんが、Gojiはテンプレートファイルを基にしたビュー生成の機能を一切備えていません。ビューのためのテンプレートエンジンが必要であれば、Goの標準パッケージである「text/template」や「html/template」などを組み込んで使用するか、あるいはビュー機能を提供してくれる外部パッケージを導入するなどの作業が必要になります。

 ハンドラを作成したら、次にどのようなURLパスから処理が呼び出されるかを定義します。Gojiは net/http パッケージよりも柔軟性が高いルーティング定義方法を備えています。

// GETメソッドで/hello/:name にマッチする場合のルーティング
goji.Get("/hello/:name", xxxxx)
// 複数のURLパラメータも定義できる
goji.Get("/articles/:year/:month/:day", xxxxx)
// Postメソッドの場合
goji.Post("/articles/post", xxxxx)
// * を使ったルーティング(後続のパスはハンドラー内から c.URLParams["*"] で取得できる)
goji.Get("/prefix/*", xxxxx)
// 正規表現(名前付きグルーピングで /pages/100 の場合はパラメータ id=100 が取得できる)
goji.Get(regexp.MustCompile(`^/pages/(?P<id>¥d+)$`), xxxxx)
// httpモジュールで定義済みのNotFoundハンドラを使う
goji.Get("/not_found", http.NotFound)

 

 このようにハンドラを書いて、それをルーティングに定義するという流れがGojiを利用したWebアプリケーション開発の中心的な作業になります。ルーティング定義が終わったら goji.Serve() を呼び出すことでWebサーバの実行が開始されます。

goji.Serve()

 

 GojiのWebサーバは8000番ポートで起動しますが、このポート番号を変えるインターフェースは goji には存在しません。細かい仕組みは省略しますが goji.Serve() を使用してポート番号やバインドするIPアドレスを変更する場合は、ビルドが完了した実行ファイルに -bind オプションを指定して実行する必要があります。

 仮に実行ファイル名が websrv.exe で、Webサーバを8080番ポートで起動する場合は下記のように実行します。

C:¥> websrv.exe -bind :8080

 

 Gojiにはここまでで紹介した以外にも重要な機能があります。より深く知りたい場合は「zenazn/goji」の「exapmle」フォルダ以下のサンプルが役に立ちます。

 

JSON操作の基本

 近年のWeb APIにおいて、クライアント・サーバ間での情報のやり取りには「JSON」が事実上の標準として使用されます。ここでは、GoでJSONフォーマットを取り扱う方法について解説します。

json.Marshal

 GoでJSONフォーマットを操作するには、標準パッケージ「encoding/json」を使用します。次に示すコードでは「ユーザID」「名前」「メールアドレス」の3つのフィールドを持つ User 構造体を作成し、その内容をJSONフォーマットへ変換しています。

package main

import (
    "fmt"
    "encoding/json"
)

type User struct {
    Id int
    Name string
    Email string
}

func main() {
    user := &User{1, "Yamada", "yamada@example.com"}
    bs, _ := json.Marshal(user)
    fmt.Printf("%s", bs)
}

// 出力結果
// {“Id”:1,”Name”:”Yamada”,”Email”:”yamada@example.com”}

 

 構造体からJSONフォーマットへ変換するといっても、単純な構造体を json.Marshal() に渡しているだけです。簡単ですね(コードを簡略化するため、json.Marshal() のエラー処理は省略しています)。

 上記の例では、構造体のフィールド名がそのままJSONフォーマットの連想配列のキー値にマッピングされており、便利といえば便利なのですがマッピング先を個別に指定できないのは困りものです。そこで User 構造体に「タグ」を追加してみます。

type User struct {
    Id int        `json:"user_id"`
    Name string   `json:"user_name"`
    Email string  `json:"user_email"`
}

 

 Goでは、構造体の各フィールドに対して任意の「タグ」を追加することができます。encoding/json パッケージは、構造体に付加された「 json 」から始まるタグの内容を、連想配列のキー値としてマッピングします。もう一度プログラムを実行して、出力結果がどのように変わるか確かめてみましょう。

{"user_id":1,"user_name":"Yamada","user_email":"yamada@example.com"}

 

 Id フィールドは user_id へ、その他のフィールドも指定した内容でマッピングされました。

json.Unmarshal

 Goの構造体からJSONフォーマットへの変換が成功したので、次は逆パターンを試してみましょう。

package main

import (
    "fmt"
    "encoding/json"
)

type User struct {
    Id int        `json:"user_id"`
    Name string   `json:"user_name"`
    Email string  `json:"user_email"`
}

func main() {
    var u User
    str := `{"user_id":1,"user_name":"Yamada","user_email":"yamada@example.com"}`
    json.Unmarshal([]byte(str), &u)
    fmt.Printf("%#v", u) // "%#v"を使用すると構造体のフィールド名も出力される
}

// 出力結果
// main.User{Id:1, Name:"Yamada", Email:"yamada@example.com"}

 

 json.Unmarshal()[]byte 型に変換したJSONフォーマットを与えると、第2引数に指定したポインタが指す構造体に情報を書き出すことができます。json.Marshal() と同様に非常にシンプルなコードで書けることが分かりました。

次のページ
データベースとSQL

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

松尾 愛賀(マツオ アイガ)

株式会社ラクーンの技術戦略部で働くエンジニアです。主に新事業系の技術基盤などを担当してます。RubyとGoとAWSとDebian系ディストロが好み。・株式会社ラクーン技術ブログ:http://techblog.raccoon.ne.jp/

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/8374 2015/01/09 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング