go-endpoints
AppEngineではRESTサーバと、そのクライアントライブラリを簡単に作るための仕組みとして、
Google Cloud Endpointsを機能として提供しています。
現状は公式ではGAE/J、GAE/P(Python)のみ利用可能で
一度個人的にGAE/Jで使ってみたのですが、
Spin-upの問題などで、ちょいちょいスピードが気になっていました。
そこである方から、Go言語のサードパーティ製Clound Endpoints実装があることを教えていただいたので、
ちょろちょろ書いてみたいと思います。(ほとんどreadmeと同じですが...)
Intro
go-endpointsはGo言語のサードパーティ製Clound Endpoints実装で、
Cloud Endpointsで利用させるGoogle Discovery Service用のJSONの作成や、
APIの作成フレームワークを提供します。
Install
Goや、appengine-goのインストールは省きます。
まずgo-endpointsを取得します。
go get github.com/crhym3/go-endpoints/endpoints
多分取得時に以下の様なエラーが吐かれますが、特に問題ないです。
package appengine: unrecognized import path "appengine"
package appengine/user: unrecognized import path "appengine/user"
package appengine_internal/user: unrecognized import path "appengine_internal/user"
現状go-endpointsはappengineパッケージに依存していますが(依存していないforkもある)、
appengineパッケージは通常dev serverにより提供されるので、存在しなくても動きます。
使用方法
リクエストの受取用とレスポンスの返却用structの作成
まず、データの送受信用structを作成します。
このstructはgo-endpoints側でencoding/json
パッケージにてstruct ←→ JSONの変換をされます。
必要であればencoding/json
を利用する際に使う、field tagをつけておきます。
もちろん更に必要であれば、Datastore用のfield tagもつけておきます。
type Greeting struct {
Key string `json:"id" datastore:"-"`
Author string `json:"author"`
Content string `json:"content" datastore:",noindex"`
Date time.Time `json:"date"`
}
type GreetingList struct {
Items []*Greeting `json:"items"`
}
またgo-endpointsでもfield tagを提供していて、そのjson fieldの必須/非必須、デフォルト値、説明等を定義できます。
※ただ自動でデフォルト値を入れてくれたり、チェックをしてくれる感じでは無い気がする... api explorerでの説明に利用しているのみ??
type GreetingSearchReq struct {
Query string `json:"q" endpoints:"req,required, desc=The query string"`
Limit int `json:"limit" endpoints:"d=10,min=1,max=100,desc=The limit search result count"`
}
細かいfiled tagの説明はここにありますが 、reqとrequiredがコード中にはあるなど微妙に揺れている気も...
サービスの作成
サービスは実際のAPIの送受信部分の実装を行います。
import "github.com/crhym3/go-endpoints/endpoints"
type GreetingService struct {
}
// List responds with a list of all greetings ordered by Date field.
// Most recent greets come first.
func (gs *GreetingService) Search(r *http.Request, req *GreetingSearchReq, resp *GreetingsList) error {
if req.Limit <= 0 {
req.Limit = 10
}
c := endpoints.NewContext(r) //endpoints.Contextはappengine.Contextを持っている
//なんかゴニョゴニョして、greets []*Greetingを取得
resp.Items = greets
return nil
}
サービスでは任意のメソッド名で
- 第一引数が
*http.Request
- 第二引数が受信用の
struct
- 第三引数が送信用の
struct
- 返却値が
error
を定義します。
サービスの登録
サービスが作成できたら、登録をおこないます。
import "github.com/crhym3/go-endpoints/endpoints"
func init() {
//サービスのインスタンスを作って RegisterServiceを呼び出し
greetService := &GreetingService{}
api, err := endpoints.RegisterService(greetService, "greeting", "v1", "Greetings API", true)
if err != nil {
panic(err.Error())
}
//APIは一つづつ登録していく
info := api.MethodByName("Search").Info()
info.Name, info.HttpMethod, info.Path, info.Desc =
"greets.list", "GET", "greetings", "Search most recent greetings."
endpoints.HandleHttp()
}
なお上記のinfo.Pathには/greetings/{id}
などのURLパターン(って言い方で合ってる?)も利用可能です。
app.yamlの設定
あとはyamlにendpointsの設定をすれば完了です。
application: my-app-id
version: v1
threadsafe: true
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
# ここが絶対に必要
- url: /_ah/spi/.*
script: _go_app
叩く
あとはdev serverを起動して、localhost:8080/_ah/api/explorerとかにアクセスすれば、試すことが可能です。
API Client Libraryの作成
大体ここ読めばok
※個人的にjsクライアントしか使わなかったので試してないですごめんなさい探さないでください。
その他
大体のことはパッケージドキュメントやサンプルコード読むとわかると思います。
OAuthを利用した、appengineでのユーザ認証なども利用可能です。
気になりごと
現状だと、error
を返却した場合は、必ずHTTP Statusが400で返されます。
一応Pull Req送っているので取り込まれたら、変更することも可能になるはずです。(そしたらそのあたりも追記します。)