2011/06/09

Recent entries from same category

  1. Go 言語プログラミングエッセンスという本を書きました。
  2. errors.Join が入った。
  3. unsafe.StringData、unsafe.String、unsafe.SliceData が入った。
  4. Re: Go言語で画像ファイルか確認してみる
  5. net/url に JoinPath が入った。

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
Posted at by