キノコの自省録

日々適当クリエイト

TwitterにOAuthで認証してつぶやくAS3コード

自分の探し方が悪いのか、
TwitterにOAuth認証してつぶやくAIR/Flex/ActionScript3のサンプルが見つからず、四苦八苦しました。
なんだかtwitterでAS3が適当な扱いを受けているような印象が……。
ということで、twitterに投稿するサンプルコードをメモ書き。


準備

OAuth認証して、Access Tokenを取得しておく必要があります。
前回のエントリを参照。
Access Tokenは、ユーザが連携アプリの認可を取り消さない限り有効です。

認証ヘッダ

BASIC認証と同じように、HTTPヘッダに'Authorization'ヘッダを記述する必要があります。
このヘッダを作るのが非常に面倒くさいです。
前回のエントリ同様、iotashan氏のライブラリを用います。
consumer tokenにはアプリのConsumer tokenを、
access tokenには認可されたAccess tokenを指定します。


var oauthReq:OAuthRequest = new OAuthRequest("POST", "http://api.twitter.com/1/statuses/update.xml", { oauth_version: "1.0", status: つぶやき本文},consumer token, access token);
var auth:URLRequestHeader = oauthReq.buildRequest(new OAuthSignatureMethod_HMAC_SHA1, OAuthRequest.RESULT_TYPE_HEADER);
trace(auth.toString()); //Authorizationヘッダが出力される

とてもすっきり書けてメデタシメデタシ……と言いたいところなのですが、
上記のコードは上手く動きません。
原因は、各パラメータの出現順序にあります。
oauth_で始まるパラメータは、パラメータ名の辞書順で昇順ソートされている必要がありますが、
残念ながら上記コードではソートされません。
出来上がったRequestをtraceなどで確認してみてください。
修正方法はいくつか考えられると思いますが、私が取った結構強引な方法を書いておきます。


var oauthReq:OAuthRequest = new OAuthRequest("POST", "http://api.twitter.com/1/statuses/update.xml", oauth_version: "1.0", status: つぶやき本文}, Consumer token, Access token);
var auth:String = oauthReq.buildRequest(new OAuthSignatureMethod_HMAC_SHA1, OAuthRequest.RESULT_TYPE_POST);
auth = auth.split("&").join("\",");
auth = auth.split("=").join("=\"");
auth = auth.replace(/,status=.*/, "");
var authHeader = new URLRequestHeader("Authorization", "OAuth " + auth);

これで生成されるAuthorizationヘッダは、以下のようになるはずです。
Authorization: OAuth oauth_consumer_key="Consumer key",oauth_nonce="nonce",oauth_signature="signature",oauth_signature_method="HMAC-SHA1",oauth_timestamp="10桁のtimestamp",oauth_token="Access token",oauth_version="1.0"
辞書順になっていますよね?


認証ヘッダ生成に、つぶやき本文を用いている理由は、
こちらに書かれている通り、
oauth_signatureの生成に、つぶやき本文を用いているためです。
なお、つぶやき本文は、URLEncodeしないでOAuthRequestに渡します。
なぜならば、OAuthRequest内部でURLEncodeされるため、多重にエンコードされてしまいますので、
実際のつぶやきと、認証で用いたつぶやき本文が異なり、認証が通らなくなります。


HTTP通信オブジェクト

HTTPServiceやURLLoaderなどを利用する場合、
Authorizationを含む一部のカスタムヘッダが利用できません。
as3httpclientlibを使いましょう。
BASIC認証でもAuthorizationヘッダが必要なので、
どちらを使うにしても、HTTPServiceは使えません。

statusの記述

次に、status(つぶやき本文)を書く位置ですが、
BASIC認証の時は、
http://api.twitter.com/1/statuses/update.xml?status=test
のように、URLパラメータで記述できましたが、
これは今でも、中途半端に生きています。
というのは、ASCII文字のみのPOSTならこれでいけますが、
日本語をPOSTしようものなら、
500 internal server errorが返ってきます。
URLEncodeしてもダメです。
上の例ならASCIIのみなので正常にポストされます。
「test」を「テスト」に変えてURLEncodeしたもの、すなわち、
status=%E3%83%86%E3%82%B9%E3%83%88
とすると、失敗します(500: internal server error)。
そんなわけで、statusはBodyに記述してやる必要があります。
中途半端に生きていてくれたため、ハマリました。

as3httpclientlibのHttpClientでPOSTする方法

これはソースコード見た方が早いでしょう。


var req:HttpRequest = new Post();
req.addHeader("Authorization", 上で作ったAuthorization文字列);
req.setFormData([{ name: "status", value: つぶやき本文 }]);
var client:HttpClient = new HttpClient();
client.request(new URI("http://api.twitter.com/1/statuses/update.xml"), req);

注)上記コードは簡略化して書いてあります。結果を受け取りたいならリスナーを登録しましょう。
メッセージボディーへの書き込みには、必ずsetFormDataを利用するようにしましょう。
HttpRequest.bodyに直接書き込むと、理由は不明ですが、通信がハングアップします。
setFormDataのvalueは、自動的にURLEncodeされますので、
URLEncodeせずに、生で渡すようにしましょう。


リターンコード

401が返ってきたら、Authorizationの値(特にシグネチャ!)が間違っている可能性が高いです。
500が返ってきたら、リクエストの構成が間違っている可能性があります。
401は原因が突き止めやすいですが、500は恐怖です。

なんだか非常に苦労したんですが、
みんなあっさり解決してるんですかね……?
他の言語はライブラリが充実しているのに、
AS3がなんか不遇だってせいもあるのでしょうけど。
HTTPServiceはAuthorizationヘッダを記述できないとか、AS3特有の問題もありますが。