えいのうにっき

a-knowの日記です

GAE/Go で gomniauth を使う

最近また Google App Engine をやっている。

cloud.google.com

できるだけ面倒なことはしたくないんだけど、作ろうとしているものの性質上、ログイン機能は外せず...。。『Go言語によるWebアプリケーション開発』の学習でも使った stretchr/gomniauth を使って同じように実装してみると、http.DefaultTransport and http.DefaultClient are not available in App Engine. See https://cloud.google.com/appengine/docs/go/urlfetch/ といったエラーが。urlfetch ... あー。

GAEから外部APIへリクエストをするためには、専用パッケージである urlfetch を使う必要がある。

自分で実装してる部分の話なら素直に urlfetch を使えばいいだけなんだけど、今回のエラーは gomniauth の部分で出てるエラーなので、なんとかして gomniauth の使うクライアントを差し替えてやる必要がある。

gomniauth のリポジトリを少し漁ってみたところ、いいコミット(コミットメッセージ)があった。

Support for Google AppEngine urlfetch for OAuth2

To use http in Google AppEngine, you have to use urlfetch, not the normal default http client or RoundTripper. So similar to what one of the forks of goauth2 did, this adds the ability via the now-added common/SetRoundTripper to pass in the specific RoundTripper to use. If unset, the default net/http one will be used. However, by calling this method, you can pass in your own including one created via urlfetch. Here is sample code showing it in use:

https://github.com/stretchr/gomniauth/commit/3e2e23995b035e26bbd58a0f56cb2b2d61dbe993#diff-faa162d8416db3f39bdfe91ca8eea4de

上のコミットメッセージに付記されていた実装例は以下。

import (
  "appengine"
  "github.com/stretchr/gomniauth/common"
)

func SomeGolangFunc([...,] r *http.Request [,...]) {
  c := appengine.NewContext(r)
  t := new(urlfetch.Transport)
  t.Context = c
  common.SetRoundTripper(t)

  // Do something interesting here
}

これを参考に自分の実装も手直ししてみたところ、うまくいった。よかった。

上記のコミットメッセージには、以下のような注意点も加えて書かれていたので、この点は気をつけておいたほうがよさそう。

Note that common.SetRoundTripper may have to be called in the GAE server before every request, versus trying to do it once because of the appengine.Context being passed in. Pretty sure you cannot pass in a nil *http.Request and re-use the context, so you can't make the call during the GAE init() method.

今回の件にあたり、いろいろとググったりもしてみたところ、gomniauth に限らず、いろんな実装でこうした配慮がされてそうなので、このまま安心して実装を続けていけそう。