go-endpointsを触ってみた

go-endpoints
========================

AppEngineではRESTサーバと、そのクライアントライブラリを簡単に作るための仕組みとして、
[Google Cloud Endpoints](https://developers.google.com/appengine/docs/java/endpoints/)を機能として提供しています。

現状は公式ではGAE/J、GAE/P(Python)のみ利用可能で
一度個人的にGAE/Jで[使ってみた](https://github.com/soundTricker/gas-library-box-server)のですが、
Spin-upの問題などで、ちょいちょいスピードが気になっていました。

そこである方から、Go言語のサードパーティ製Clound Endpoints実装があることを教えていただいたので、
ちょろちょろ書いてみたいと思います。（ほとんどreadmeと同じですが...）

# Intro

[go-endpoints](https://github.com/crhym3/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](https://github.com/rwl/go-endpoints/)もある)、
appengineパッケージは通常dev serverにより提供されるので、存在しなくても動きます。

# 使用方法

## リクエストの受取用とレスポンスの返却用structの作成

まず、データの送受信用structを作成します。  
このstructはgo-endpoints側で`encoding/json`パッケージにてstruct ←→ JSONの変換をされます。
必要であれば`encoding/json`を利用する際に使う、field tagをつけておきます。
もちろん更に必要であれば、Datastore用のfield tagもつけておきます。

```go:送信(Response)用struct.go
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での説明に利用しているのみ？？

```go:受信(Request)用struct.go
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の説明は[ここ](https://github.com/crhym3/go-endpoints/wiki/Field-tags)にありますが　、reqとrequiredがコード中にはあるなど微妙に揺れている気も...

## サービスの作成

サービスは実際のAPIの送受信部分の実装を行います。

```go:Service.go
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`

を定義します。

## サービスの登録

サービスが作成できたら、登録をおこないます。


```go:main.go

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](http://localhost:8080/_ah/api/explorer)とかにアクセスすれば、試すことが可能です。


## API Client Libraryの作成

大体[ここ](https://github.com/crhym3/go-endpoints/wiki/Getting-Started-%28readme%29#generate-client-libs)読めばok
※個人的にjsクライアントしか使わなかったので試してないですごめんなさい探さないでください。

## その他

大体のことは[パッケージドキュメント](http://godoc.org/github.com/crhym3/go-endpoints/endpoints)や[サンプルコード](https://github.com/crhym3/go-tictactoe)読むとわかると思います。  
OAuthを利用した、appengineでのユーザ認証なども利用可能です。

# 気になりごと

現状だと、`error`を返却した場合は、必ずHTTP Statusが400で返されます。
一応Pull Req送っているので取り込まれたら、変更することも可能になるはずです。(そしたらそのあたりも追記します。)