Google App EngineへのGo言語アップロード権(Trusted Tester for the golang runtime for appengine)を得たので、Webアプリを作り始めたら皆が必ず作るという「おうっふー」をGo言語に移植してみた。
なお、「おうっふー」とはログイン画面のみを持ち、ログインすると有無を言わさず「おうっふー」という単語をログインアカウントにポストするWebアプリケーションの事。知ってない奴はWeb界のモグリだ!って隣の人が言ってた。
以下、Go言語のコードを解説してみる。
ログイン画面からログインハンドラに要求が行われると、OAuth1.0でリクエストトークンがTwitterに要求され、得たテンポラリトークンで認証画面に遷移します。
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
client := urlfetch.Client(appengine.NewContext(r))
tmp, err := creds.request(client)
if err != nil {
http.Error(w, err.String(), 500)
return
}
url := fmt.Sprintf("%s?oauth_token=%s&oauth_callback=%s", authURI, http.URLEscape(tmp.Key), http.URLEscape(callbackURI))
w.Header().Set("Set-Cookie", "tmp="+http.URLEscape(tmp.Key)+"/"+http.URLEscape(tmp.Secret))
http.Redirect(w, r, url, 302)
})
この時、注意しなければならないのがurlfetchにリクエストから作成したコンテキストが必要である事。urlfechを使いたい関数までhttp.Requestを引きずり回さないといけないという、なんとも煩わしい仕様ですがキマリはキマリ。
また認証画面から戻って来た時に再び照合が行える様に、テンポラリトークンをクッキーに保持しておきます。
認証画面から戻ってくると登録していたコールバックURLが呼び出されます。
client := urlfetch.Client(appengine.NewContext(r))
v := r.FormValue("oauth_verifier")
a := strings.Split(r.Header.Get("Cookie"), "=", -1)
w.Header().Set("Set-Cookie", "tmp=")
if len(a) != 2 || a[0] != "tmp" {
http.Error(w, "invalid request", 500)
return
}
a = strings.Split(a[1], "/", -1)
if len(a) != 2 {
http.Error(w, "invalid request", 500)
return
}
ak, _ := http.URLUnescape(a[0])
as, _ := http.URLUnescape(a[1])
tmp := &Token{ak, as}
token, err := creds.verify(client, tmp, v)
if err != nil {
http.Error(w, err.String(), 500)
return
}
クッキーを読み取り得たテンポラリトークンと、認証画面から戻されたベリファイアを合わせて照合を行います。Twitter APIを使ったアプリをバシバシ書いてる諸君なら、鼻くそほじるより簡単ですね。
後は得られたアクセストークンを使ってTwitterにポストする。
gouffu := []string{"ごうっふ~", "おうっふ~", "もうっふ~", "とうっふ~", "もふもっふ~", "わっふ~", "きゃっふ~"}
data := map[string]string{"status": gouffu[rand.Int()%len(gouffu)]}
creds.sign(token, data, resURI, "POST")
resp, err := client.PostForm(resURI, data)
if err != nil {
http.Error(w, err.String(), 500)
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
http.Error(w, resp.Status, 500)
return
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
http.Error(w, err.String(), 500)
return
}
var m map[string]interface{}
err = json.Unmarshal(b, &m)
if err != nil {
http.Error(w, err.String(), 500)
return
}
screen_name := m["user"].(map[string]interface{})["screen_name"].(string)
id_str := m["id_str"].(string)
url := fmt.Sprintf("https://twitter.com/%s/status/%s", screen_name, id_str)
http.Redirect(w, r, url, 302)
単語1つでは遊び心が無いので、何個か用意した。ポストが完了したらパーマリンクへリダイレクトする様にした。
なお、このサーバではクッキーは一時的に保存する事はしてもデータストア等には何も残していません。
ごうっふ~
...
http://go-ouffu.appspot.com/
良かったら遊んでみて下さい。
ソースはこの辺に置いときます。
mattn/go-ouffu - GitHub
ごうっふ~
https://github.com/mattn/go-ouffu