umegusa's blog

備忘録

golangでoauth2を使ってFacebookAPIを叩いてみるよ!!

お前いつもoauth認証やってんな、という突っ込みもお待ちしております。
Facebookは未経験なのもあって結構てこずりました。

revelフレームワークを使ってfacebook認証っぽい何かを実装する

ぶっちゃけ使わなくても全然いけます(確信

いつものようにアプリケーションを作成しましょ。

revel new facebookApp

ルーティングなどは結構適当に作っていいと思います。

TwittweAPIとの違いはFacebookはoauth2を使って認証することです。
oauth2.0のほうが結構楽な気はしてます、毎回面倒な証明書作らなくていいみたいだし。

また、oauth2.0用のライブラリは1.0とは違うライブラリを使います。
golang/oauth2 · GitHub
公式なのかな?
goauth2を使ったやつは結構転がってたんですが、oauth2を使ったやつが全然なかったんですよね。。。

でもgoauth2 is deprecated.とあったのでoauth2を使うしかないような気がしてます。
goauth2 - Go OAuth 2.0 - Google Project Hosting

ということでoauth2を使って認証していくべし!!

認証自体は上記ライブラリを使えばめっちゃ簡単にできます。
ルーティングやらファイル構成は前回の記事を参考にしてみてください。

revelフレームワークを使ってTwitterAPIを叩く! - umegusa's blog

Facebookアプリの登録を行う

下記のサイトからアプリの登録を行います。
Facebook Developers
Developer登録はアカウント認証済みじゃないとだめっぽいので携帯電話の登録とか行いましょう。

作成したらlocalhostでもcallbackできるように設定します。
第一のつまずきポイント。

指定されたURLは、アプリケーションの設定で許可されていません。 
指定されたURLの中にアプリの設定で認められないものが含まれています。
ウェブサイトまたはキャンバスのURLと一致しているか、ドメインがアプリのドメインのサブドメインである必要があります。

登録しないで認証しようとすると上記のように怒られます。
設定はアプリのDashBorad -> Settingsから設定します。f:id:umegusa:20150308233554p:plain
App Domainsにlocalhost, Add PlatformではURLにhttp://localhost/を設定します。
これで、アプリの設定は完了です。

FacebookAPIを叩く!

というわけで実装部分をみてみましょー。
今回は認証を通した人のメールアドレスを取得します。
auth.go

var facebook = newConfig()

func newConfig() *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     "xxxxxxxxxxxxxxxxxxxx",
		ClientSecret: "xxxxxxxxxxxxxxxxxxxx",
		RedirectURL:  "http://localhost:9000/auth/callback",
		Endpoint: oauth2.Endpoint{
			AuthURL:  "https://www.facebook.com/dialog/oauth",
			TokenURL: "https://graph.facebook.com/oauth/access_token",
		},
		Scopes: []string{"email"},
	}

	return c
}

最初にoauth2.Configを定義します。
ClientID,ClientSecretはクライアントアプリのAppID, Secretを指定してください。

んでまずはリクエストトークン(?)を取得しに行きます。

func (c Auth) Index() revel.Result {
	// facebookから情報を取得する
	url := facebook.AuthCodeURL("")
	return c.Redirect(url)
}

AuthCodeURLメソッドを呼ぶとAuthURLで設定したURLが呼び出されます。
んでユーザ認証画面に飛ばされ、RedirecdURLに設定したURLにcallbackされます。

codeパラメータが付加されているので、それを受け取るアクションメソッドも用意します。

func (c Auth) Callback(code string) revel.Result {
	// codeを取得したらユーザ情報を取得

	// アクセストークンを取得
	tok, err := facebook.Exchange(oauth2.NoContext, code)
	// エラー処理は必須
	if err != nil {
		return c.Redirect(App.Index)
	}

	// ユーザ情報を取得
	client := facebook.Client(oauth2.NoContext, tok)
	result, err := client.Get("https://graph.facebook.com/me")
	if err != nil {
		// 失敗
		revel.ERROR.Println("アクセストークン取得失敗!!", err)
	}

	// 関数を抜ける際に必ずresponseをcloseするようにdeferでcloseを呼ぶ
	defer result.Body.Close()

	//jsonをパースする
	account := struct {
		MailAddress string `json:"email"`
	}{}
	_ = json.NewDecoder(result.Body).Decode(&account)

	fmt.Println(account.MailAddress)

	// 取得したhtml(json)を確認する方法
	// byteArray, _ := ioutil.ReadAll(result.Body)
	// fmt.Println(string(byteArray)) // htmlをstringで取得

	return c.Redirect(Show.Index)
}

Exchangeメソッドに帰ってきたコードを渡すことでアクセストークンを取得できます。
TokenURLに設定したURLにcodeパラメータを付加してアクセスしていますね。
err処理を実装しないとエラーでめっちゃ怒られます。
つまずきポイント2でした。

その後はトークンをhttpClientに突っ込んでアクセスすればオッケーです。

あとはencoding/jsonの処理で必要な値のみ取得して終わりです。
さくっとできますね!
途中deferでcloseを呼んでいますが、閉じる必要のあるものは必ず呼び出すようにしましょう。

実行するとアクセスログになんかメールアドレスが表示されているような気がします。

ちなみにFacebookも2.0で習得できるパーミッションの種類が大幅に削減されたみたいです。

  • 公開プロフィール情報)public_profile)
  • メールアドレス(email)
  • いいね!リスト(user_likes
  • 友達リスト(user_friends)

ちなみにこの情報はConfigのScopeに設定する項目になります。
上記を設定することで、使用するユーザにどの権限を使うかを明示的にします。

終わりに

Twitterに比べるとさくっと取れそうな感じですね。
GraphAPI全然勉強してないから何取れるか全然分からないけど
oauth2.0のほうがさくっと取れそうなのでSwiftとかでもチャレンジしてみたいっすね。
最近のサービスはoauth2.0採用してるところが多いので
他のもこんな要領で取れるのではないでしょうか。

今回のソースコードもべしっと。
kaisou4537/revelFacebookApp · GitHub


以上!

revelフレームワークを使ってTwitterAPIを叩く!

golangフレームワークrevelを使用してTwitterAPIを叩きます。

アプリケーションの作成

revelフレームワークでまずはアプリケーションを作成しましょう!

$ revel new twitterApp

そうすると$GOPAT/src/twitterAppが作成されるはずです。
※ revelフレームワークの導入はこちらから。

revelフレームワークを入れてみる - umegusa's blog

Routingの設定

まずはルーティングを設定します。
今回はこのように設定しました。
$GOPATH/src/twitterApp/conf/route

GET     /                                       App.Index
GET     /auth                                   Auth.Index
GET     /auth/callback                          Auth.Callback
GET     /show                                   Show.Index

動きとしては、

URL 動作
http://host/ AppコントローラのIndexアクションメソッドを呼び出す
http://host/auth/ AuthコントローラのIndexアクションメソッドを呼び出す
http://host/auth/callback/ AuthコントローラのCallbackアクションメソッドを呼び出す
http://host/show/ ShowコントローラのIndexアクションメソッドを呼び出す

こんな感じに設定します。
それではコントローラをいじってみましょう。
$GOPAT/src/twitterApp/app/controller/app.go

package controllers

import "github.com/revel/revel"

type App struct {
	*revel.Controller
}

func (c App) Index() revel.Result {
	return c.Render()

/でアクセスするとこちらのIndex()メソッドが呼び出されます。
返り値としてはrevel.Resultを返します。
returnで返しているのは、views下のhtmlファイルを返します。
この場合、$GOPAT/src/twitterApp/app/views/App/Index.html を返します。
基本的に$GOPAT/src/twitterApp/app/views/{コントローラ名}/{アクションメソッド名}.html を返すみたいですよ。
中のHTMLは標準を少しだけいじりました。
Index.html

 <div class="container">
    <div class="row">
      <div class="hero-text">
        <h1>Twitter認証するよ!!</h1>
        <p><a href="/auth/">Twitterで認証するよ!</a></p>
      </div>
    </div>
  </div>

実行してみる

とりあえず実行するとこんな感じです。
実行方法は下記のように行います。

$ revel run twitterApp

画面だとこんな感じです。
http://localhost:9000/
f:id:umegusa:20150306011641p:plain
$GOPATH/src/twitterApp/conf/app.conf下をいじっていなければ9000番のポートで実行できるはずです。
続いてOAuth認証を実装してみましょう。

oauth認証をする

TwitterのOAuthのバージョンは1.0で、それを簡単にしてくれるライブラリを使用します。
それがこちらです。
mrjones/oauth · GitHub

実際に使用する前にまずはクライアントアプリケーションを登録し、consumerkey, consumersecretを取得する必要があります。
クライアントの登録は下記のページから作成できます。
Twitter Application Management
作成した際は必ずCallback URLを必ず設定してください。(設定しないとoauth_callbackパラメータが無効になり、コールバックが無効になってしまいます)
値はURLの形であれば適当で問題ありません(ex: http://twitter.com/kaisou4537/)

では実際に使ってみます。
controller下にauthコントローラを作成します。
auth.go

package controllers

import (
	"encoding/json"
	"github.com/mrjones/oauth"
	"github.com/revel/revel"
	"twitterApp/app/models"
)

type Auth struct {
	*revel.Controller
}

// oauthのコンシューマ設定
var twitter = oauth.NewConsumer(
        // 先ほど取得したキーを使用する
	"consumerkey",
	"consumersecret",
	oauth.ServiceProvider{
		AuthorizeTokenUrl: "https://api.twitter.com/oauth/authorize",
		RequestTokenUrl:   "https://api.twitter.com/oauth/request_token",
		AccessTokenUrl:    "https://api.twitter.com/oauth/access_token",
	},
)

func (c Auth) Index() revel.Result {
	// Twitterから情報を取得する
	user := getUser()

	// callback URLを設定する
	requestToken, url, err := twitter.GetRequestTokenAndUrl("http://localhost:9000/auth/callback")
	if err == nil {
		// ユーザ情報セット
		user.RequestToken = requestToken
		// oauth_verifierを取得
		return c.Redirect(url)
	}
	revel.ERROR.Println("リクエストトークン取得できませんでした!!", err)

	return c.Render()
}

func (c Auth) Callback(oauth_verifier string) revel.Result {
	// セットしたユーザ情報取得
	user := getUser()

	// access_tokenを獲得
	accessToken, err := twitter.AuthorizeToken(user.RequestToken, oauth_verifier)
	if err == nil {
		// ユーザ情報を取得する
		resp, _ := twitter.Get(
			"https://api.twitter.com/1.1/account/verify_credentials.json",
			map[string]string{},
			accessToken,
		)
		defer resp.Body.Close()
		account := struct {
			Name            string `json:"name"`
			ProfileImageURL string `json:"profile_image_url"`
		}{}
		_ = json.NewDecoder(resp.Body).Decode(&account)

		// 表示用情報をセット
		setUserData(account.Name, account.ProfileImageURL)
	} else {
		// 失敗
		revel.ERROR.Println("取得失敗!!", err)
	}

	return c.Redirect(Show.Index)
}

func (c Auth) Show() revel.Result {
	// ユーザ情報取得
	user := getShowUser("kaisou_test")
	return c.Render(user)
}

// Twitterユーザ情報
func getUser() *models.User {
	return models.FindOrCreate("kaisou")
}

// 表示用ユーザ情報セット
func setUserData(name, imgURL string) {
	models.CreateShowUser(name, imgURL)
}

~/app/models 下に下記の二つのファイルも作成します。
userinfo.go

package models

import "github.com/mrjones/oauth"

// Twitter用ユーザ情報
type User struct {
	Username     string
	RequestToken *oauth.RequestToken
	AccessToken  *oauth.AccessToken
}

var db = make(map[string]*User)

func FindOrCreate(username string) *User {
	if user, ok := db[username]; ok {
		return user
	}
	user := &User{Username: username}
	db[username] = user
	return user
}

showuser.go

package models

// 表示用ユーザ情報
type ShowUser struct {
	Username string
	ImgURL   string
}

var dbShowUser = make(map[string]*ShowUser)

func CreateShowUser(name, imgURL string) {
	user := &ShowUser{Username: name, ImgURL: imgURL}
	dbShowUser[name] = user
}

func FindShowUser(name string) *ShowUser {
	if user, ok := dbShowUser[name]; ok {
		return user
	} else {
		return nil
	}
}

auth.goでは、まずリクエストトークンを取得します。
その後、設定したコールバックURLにアクセスさせ、アクセストークンを取得、成功したらAPIをたたくという流れになります。

auth.goで取得したデータをリクエストトークンを格納、アクセストークンを取得するのに必要になります。
そして、アクセストークンを使用して取得したTwitterの情報をshowuser.goに格納し、View側のHTMLに渡してあげます。

auth.goのCallbackメソッドの最後にShowコントローラのIndexアクションメソッドにリダイレクトしていますね。
show.goは下記のようになっています。

package controllers

import (
	"github.com/revel/revel"
	"twitterApp/app/models"
)

type Show struct {
	*revel.Controller
}

func (c Show) Index() revel.Result {
	// ユーザ情報取得
	user := getShowUser("kaisou_test")
	return c.Render(user)
}

// 表示用ユーザ情報取得
func getShowUser(name string) *models.ShowUser {
	return models.FindShowUser(name)
}

auth.goで格納した情報を取得し、Viewに渡しています。
プログラムで処理した後のViewの値はRenderメソッドに渡してあげることでViewで扱えるようになります。
尚、使用する場合は下記のように呼び出します。
~/app/views/Show/Index.html

<div>
  <img src="{{.user.ImgURL}}">
  <p>{{.user.Username}}</p>
  <p>めっちゃ小さいけどとれてるね!!!!!</p>
</div>

実行して表示してみると画像のようになります。
f:id:umegusa:20150306011645p:plain

ユーザ情報が取得され、表示されているのが分かりますね!
これでTwitterAPIをたたいてユーザ情報を取得することができました!

終わりに

こんなにめんどくさいことしなくてもDBにaccesstoken格納してAPI呼び出せるようにしとけばええやん、という突っ込みは今回はなしでお願いします。。
とりあえずテスト的に取ることができました、ぱちぱち。
APIにアクセスして値を取ることができればTeitterAPIを使用したアプリケーションの作成は攻略したも同然なので、
あとはどのようなサービスにするかだけ考えて実装すればいいんじゃないでしょうか!

はまったこととしては

  • importに未使用のパッケージを定義するとエラーになる
  • 変数などに格納して1度も使用しないとエラーになる
  • 読み込んでいるパッケージ群の中で同じメソッド名で定義していた場合はエラーになる

ということでした。。

厳しいと思いつつ、かなり厳密なプログラムが出来上がるのでとてもいいと思います(毎回プログラムが雑すぎるということなので注意しなければならないですね。。)

最後にソースコードをあげておきます。
kaisou4537/revelTwitterApp · GitHub

以上、長々とありがとうございました!

ヒカルのgo#4に参加してきました!

golang触り始めてまだ1,2週間とかで
A Tour of Goを一通りみて演習課題をすっ飛ばしつつ進めていた私ですがgolangの勉強会に参加してきました。
今回参加してきたのはこちら。

ヒカルのgo! #4 (hikarie.go) - connpass

golang初心者〜中級者向け勉強会です。

会場の雰囲気と参加層

雰囲気はそんなに固い感じでもなく発表自体も初心者向けということで初心者でも割と理解できる内容でした。
golangも触ったことない人が半分以上でとりあえず聞いてみようの精神の人が多かったんじゃないかなーと。
懇親会はおいしいご飯食べながらだらだらしゃべる感じでいい雰囲気でした。

参加してみて

結論から言うとwebアプリケーション作成を行う場合は茨の道だなーという印象でした。
理由はDB処理がとても苦手ということ、ベストプラクティスがないことの二つです。
API開発とかには使えそうだけどこったWebアプリケーションの作成は修羅の道なるんじゃないかな、、、
ただ低レイヤーなアプリケーション(コンソールアプリケーションやらクライアントアプリケーションやら)などは活躍がかなり期待できそうな感じです。
Cっぽく書けそうだしそっちの人も入りやすい言語だよと言うお話を伺いました。

goも1.1が出てやっとメジャーになってきましたがまだノウハウなどは全然足りないような感じみたいです。
話題になっている言語なので盛り上げていきたいですよね!

発表内容

今回の勉強会では発表/LT/ハンズオン/懇親会の4部構成でした。
発表内容はこんな感じでした。

RDB

golangSQLをたたくには?というお話でした。
結論としては銀の弾丸はない、ということでこれです!!という決定版がないことです。
これがあるのでDBの扱いは苦手なのかなと感じています。
goでSQLを扱う方法は主に3つあるみたいです。

  • SQLをたたく

自分でSQLを作成してたたくという方法です。
取得した値を構造体に突っ込むんですが、そのマッピングなどは手動で行う必要があります。
使用するライブラリは標準のものになります。

  • SQLクエリビルダーを使用する

SQLよりも抽象度が高くなったものです。
マッピングは自身で行う必要がありますが、SQLの作成を助け、SQLをオブジェクトとして作成し、そのオブジェクトを渡して実行するという動きをします。
クエリの作成が簡単にでき、クエリのタイプミスを減らすことができたり、再利用可能なクエリを書き出したりできます。

ただし、非対応なSQL構文もあるみたいです。

  • サブクエリ
  • SELECT CASE
  • UNION

これらに対応しているライブラリは現在ないそうです。
また、リレーション(JOIN)にも対応していないみたいなのですが、生のSQLも埋め込むことができるのでその辺の若干の手間が発生します。
それでも、かゆいところに届かないですが、クエリの作成、実行などは楽になりそうな気がしました。

squirrelというライブラリを使用すると下記のように書くことができます。

import sq "github.com/lann/squirrel"

users := sq.Select("*").From("person").Where(sq.Eq{"name": []string{"hoge", "fuga"})
sql, args, err := users.ToSQL()

// db.Query("SELECT * FROM person WHERE name IN (?,?)","hoge", "fuga")

ちなみにプレースホルダーもsquirrelならRDBMSに対応したエンコードプレースホルダーに変換してくれるみたいです、便利

  • ORM

ORMの機能を全て提供してくれているのは現在gormくらいしかないとのお話でした。
gormの機能として紹介されたのはこんな感じです。

中国の方が開発されたらしく、中国では商業利用もされているとか。
ただし、とても高機能でコード量が多く、変な動きをすることもあるというお話です。
ORMではないですが、テーブルマッピング(取得した値を構造体にマッピング)してくれるパッケージの紹介もありました。

  • gorp
  • xorm
  • genmai

gorpの解説があったのですが、こちらは開発が停滞気味(更新が遅い)で、カジュアルにバグを踏むというお話でした・・・w
また、ORMではないので生SQLをたたく必要もあります。

結果として何がいいの?

めんどくさいかバグを踏むかの両天秤ということみたいです。
銀の弾丸はない、というのはこのことだったんですね・・・

マルチプラットフォームインタラクティブシェルを楽になる

長くなってしまったのでさくっと。
goのREPL goreについてのお話でした。
gore便利だけどwindowsで使えないんだけどどうしよう・・・
→ 作ろう!ということで作ったみたいです。
内容はWindows APIをたたいて起動させているみたいですね。

golangを楽しく学ぼう!

まずはA Tour of Goを一通りやって基本機能のパッケージ化、他の言語をgoで翻訳することから初めて行こうぜというお話でした。
Goroutineとchannelとかは後ででも問題ないとのこと、まずは基本で他の言語でもできそうなところから!ということですね。
インターフェースを制するものはgoを制するともおっしゃっていました。

スクレイピングしよう!

ハンズオンです。
hikago-4_handsOn/text01.md at master · 7yan00/hikago-4_handsOn · GitHub
golangのドキュメントをスクレイピングしようと言う内容でした。
詳細はコードをのぞいてみてください!

終わりに

ついていけるか不安がありましたが、楽しく終えることができました。
勉強会はとても敷居の高い場所だと思っていましたが、情報共有の場とか交流を深める場としても使用できそうだなーという感想です。
とりあえず初心者向け勉強会出てみて雰囲気感じるというだけでもありだと思うので、今後も勉強会とかあったら参加していきたいなーと思える勉強会でした!

パッケージ群

最後に今回の勉強会で登場したパッケージ群を軽く紹介して終わりにします。

SQLクエリビルダー。説明は上記を参照してください。

dropboxが提供しているSQLクエリビルダー。
mysqlのみ対応しています。

登壇者の方が作成されているクエリビルダー。
とても軽くとても早いそうです!

ORM。とても大きい。

テーブルマッピング

goのREPL。インタラクティブシェルのお話で出てきたライブラリです。

スクレイピングを簡単にできるライブラリ。
最後のハンズオンで紹介がありました。

以上!

revelフレームワークを入れてみる

golangでrevelフレームワークというものを入れてみたのでメモ。

revelフレームワーク

The Revel Web Framework for Go
こちらのフレームワーク
とりあえず入れるだけ入れて遊んでみます。

revelフレームワークのインストール

コマンド1発たたくだけです。

# go get github.com/revel/cmd/revel
go: missing Mercurial command. See http://golang.org/s/gogetcmd
package github.com/revel/cmd/revel
	imports github.com/agtorre/gocolorize
	imports github.com/revel/revel
	imports code.google.com/p/go.net/websocket: exec: "hg": executable file not found in $PATH

いきなり引っかかったやつです・・・・
よく見ると自分GOPATHの設定とか全然してなかったのでこちらを参考にしてパスを通す。
How to Write Go Code - The Go Programming Language

export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

さらに言うとMercurialも入れてなかったのでHomebrewでインストールします。

# brew install mercurial

改めてぽちっとな

# go get github.com/revel/cmd/revel
# revel
~
~ revel! http://revel.github.io
~
usage: revel command [arguments]

The commands are:

    new         create a skeleton Revel application
    run         run a Revel application
    build       build a Revel application (e.g. for deployment)
    package     package a Revel application (e.g. for deployment)
    clean       clean a Revel application's temp files
    test        run all tests from the command-line

Use "revel help [command]" for more information.

入ったっぽいですね。
早速アプリケーション作ってみましょう。

# revel new myapp

そうすると作業ディレクトリ内にアプリケーションができる・・・
と思っていたのですが全然できていませんでした。
ファイルどこ?って探したら$GOPATH/src 下に作成されるみたいです。

# cd $GOPATH/src
code.google.com/ github.com/      gopkg.in/        myapp/

はい、ありました。
早速実行してみましょう。

# revel run myapp

localhost:9000にアクセスするとIt's Works!と画面上に表示されています。
あとはカスタマイズしながらページ作成していけばいいんじゃないでしょうか。

ということでインストールメモでした。
githubからいろいろライブラリインストールしながら開発していくと思うので
一応そのへんの設定もできたということで開発捗りそうですね。

終わりに

readmeに設定とか説明とかフォルダ構造とか書いてあるのでそこ読んだりとか
サンプルアプリ読んでみて使い方覚えてみてから内部挙動追っかけようかなーと思ってます。

Nginx + Golang でWebアプリケーション開発を試してみた

NginxとGolangを使ってWebアプリケーションを作成してみたい!
とかそんなことを思い立ったのでチャレンジしてみました。

実行環境

OSのバージョンは下記の通りです。
サーバはサクラVPS借りてやってます。

# cat /etc/system-release
CentOS release 6.6 (Final)

CentOS 6.6ですね。

Nginxのインストール

yumを使ってインストールします。

# yum install nginx

ここで落ちてきたのがバージョンが低いものでした。
ですのでアップデートします。
まずはnginx用リポジトリを作成します。

# vi /etc/yum.repo.d/nginx.repo

内容は下記のようになります。
nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/rhel/$releasever/$basearch/
gpgcheck=0
enabled=1

そしてアップデートします。

# yum update

最後にPATHを通しておきます。
.bashrcなどに書いておくといいかもしれません。

# export PATH=$PATH:/usr/local/nginx/sbin

Nginxのインストールは以上で完了です。

Nginxの設定

NginxでWebサーバの設定を行う

続いて設定を行います。
以前はApacheで動いていたのでごっちゃにならないように設定します。

まずサーバ起動時にNginxが立ち上がるようにします。
Apacheも同様の設定をしていたので、そちらの設定は解除するような形になりますね。

# chkconfig httpd off
# chkconfig nginx on

完了したら今度はNginxを起動します。

# service nginx start

okが出たらアクセスしてみましょう。
画像のようにWelcome to Nginx!というページが出てくるはずです。
f:id:umegusa:20150222023314p:plain
ページが表示されればOKです。

Golangが動くように設定を行う

yumでインストールすると
/etc/nginx/
の中にconfigファイルが量産されてるはずです。
serverの設定を下記のように書き換えます。

# vi /etc/nginx/conf.d/default.conf

一番簡易的な設定は下記のとおりになるのかな?
default.conf

server {
    listen       80;
    server_name  hostname;

    location / {
        fastcgi_pass  127.0.0.1:9000;
        include       fastcgi_params;
    }
}

hostnameは自分のサーバのホストになります。
以上でNginx側の設定は完了です。

Golangのインストール

ダウンロードは下記ページでできます。
Downloads - The Go Programming Language

今回はwgetを使用してGolangをインストールします。

# wget https://storage.googleapis.com/golang/go1.2.2.linux-amd64.tar.gz
# tar -C /usr/local -xzf go1.2.2.linux-amd64.tar.gz

インストールが完了したらPATHを通します。
こちらも.bashrcに書いておくと問題ないかもしれません。

export GOROOT=/usr/local/go
export PATH=$PATH:/usr/local/go/bin

最後に実行できるか確認してみましょう。

# go -V

golangのバージョンなどが表示されていれば問題なくインストールされています。

GoでWebアプリケーションを作成する

今回はチュートリアルをほぼそのまま流用したものを使用して記述してみます。
index.go

package main

import (
    "fmt"
    "net"
    "net/http"
    "net/http/fcgi"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    l, err := net.Listen("tcp", "127.0.0.1:9000")
    if err != nil {
        return
    }
    http.HandleFunc("/", handler)
    fcgi.Serve(l, nil)
}

tcpソケットでListenして終わりです。
ポート番号などNginxで設定したものとあわせてください。

Webアプリケーションを実行する

今回は /var/golang/ 下に作成したプログラムを格納しました。(多分どこでも大丈夫だと思います)

作成したプログラムを実行します。

# cd /var/golang/
# go run index.go

もしくは

# cd /var/golang/
# go build index.go
# ./index

で実行できます。
あとはプログラムで設定したURLにアクセスして、
表示されれば完了になります。

バックグラウンドで実行しておく

実行コマンドの後に & をつけることでバックグラウンドで実行されるようになります。

# ./index &

プロセスを殺すときはkillコマンドを実行しましょう。

# jobs
# kill %job_number

以上です。

終わりに

ゴーファー君かわいいですね。
軽くしか触っていないのですが、Golang思ったよりさくさくかけそうな印象をうけました。
Swiftと合わせて勉強していきたいなと思います。

SwiftでTwitter OAuthを取得する access_token編

ついにTwitterAPIの認証部分をクリアしてタイムラインを取得するところまでできましたのでメモ。
ここまでできればあとは整えてクリアだ!!とか思ってたらまた壁にぶつかったので今日はここまでにしてこれまでの内容をまとめます。

oauth_tokenを使用してaccess_tokenを取得する

前回の記事ではrequest_tokenにパラメータを投げてトークンを取得するところまでやりました。

SwiftでTwitter OAuthを取得する oauth_token編 - umegusa's blog

今回はここの続きです。

request_tokenを取得して認証とaccess_tokenを取得するところまでやります。

まず、トークンを取得します。
ここは前回のコードを参照してください。

 // 非同期通信開始
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
            response, data, error in
            
            if(error != nil){
                // エラー文言表示
                println(error.description)
            }
            // oauth_token表示
             print(NSString(data: data, encoding: self.dataEncoding)!)
            
            // Authorize
            self.authorizeClient(NSString(data: data, encoding: self.dataEncoding)!)

        }

トークンを取得したらAuthorizeClientメソッドに飛びます。
Authorizeは下記のように実装します。

    // Authorize
    func authorizeClient(param: String)
    {
        var divideParam = param.componentsSeparatedByString("&")
        var oauthToken = divideParam[0].componentsSeparatedByString("=")[1]
        

        let queryURL = NSURL(string: self.authorize_url + "?oauth_token=\(oauthToken)")

        // safariで認証
        UIApplication.sharedApplication().openURL(queryURL!)
    }

本当はWebViewを使って認証したかったのですが、今回はsafariを使用してそのまま認証させます。
Authorizeはrequest_tokenで取得したパラメータをgetメソッドで投げるだけです。
そうすると認証画面に飛ぶことができます。
ただし、ここでの注意点は下記2点です。

  • アプリケーション作成時に何でもいいからコールバックURLを入力する
    • これを入れないとoauth_callbackパラメータが無効になってしまう
  • oauth_callbackパラメータはプロトコル部分だけだとうまくいかない
    • 前準備としてカスタムURLスキーマを設定
    • ex) oatuh-swift://で設定している場合
      • oauth-swift:// ×
      • oauth-swift:/// ○
      • 尚、URLは自由に設定できる

うまくいくとsafariが立ち上がって下記認証画面へ進み、アカウントで認証が成功したらアプリに戻ってきます。
よく見る画面ですね。
f:id:umegusa:20150216005137p:plain

コールバックされるとoauth_token, oauth_verifierのパラメータが付与されたURLが返ってきます。

oauth-swift://hogehoge/?oauth_token=sakldsjalkdjdkljfasldf&oauth_verifier=klajlfhjagdkasgf

※ 上記パラメータはダミーです

カスタムURLでアクセスされた情報を取得する

AppDelegate.swiftに下記のようにapplicationメソッドを用意することで取得することが可能です
AppDelegate.swift

    func application(application: UIApplication!, openURL url: NSURL!, sourceApplication: String!, annotation: AnyObject!) -> Bool {
        ViewController.handleOpenURL(url)
        return true
    }

今回はViewControllerにhandleOpenURLという静的メソッドを実装してそちらに飛ぶように実装しています。
(内容がひどいのでNSNotificationCenterをうまく使って実装したい。。。)
尚、静的メソッドは下記のように実装できます。

    class func handleOpenURL(url: NSURL) {
        println("OK!!")
    }

class func メソッド名 で静的メソッドの実装ができます。
ここでoauth_token, oauth_verifierを使用してaccess_tokenを取得しにいきます。

access_tokenを取得する

ここまで来たらもう少しです!
access_tokenの取得方法は基本的にリクエストトークンの取得と同じです。
ただし、証明書の発行や送信に使用するパラメータに差異があります。

    // get access token
    func postAccessTokenWithRequestToken(url: NSURL){
        // 基本的にリクエストトークンと同じだが、リクエストに差異がある
        
        // ここで設定するのは下記パラメータ
        // - oauth_token
        // - oauth_verifier
        var param = Dictionary<String, String>()
        var query : String = url.query!
        var divideParam = query.componentsSeparatedByString("&")
        for paramStr in divideParam {
            var splitParam = paramStr.componentsSeparatedByString("=")
            param[splitParam[0]] = splitParam[1]
        }
        
        // oauth_param
        param["oauth_version"] = "1.0"
        param["oauth_signature_method"] = "HMAC-SHA1"
        param["oauth_consumer_key"] = self.consumer_key
        param["oauth_timestamp"] = String(Int64(NSDate().timeIntervalSince1970))
        param["oauth_nonce"] = (NSUUID().UUIDString as NSString).substringToIndex(8)
        param["oauth_signature"] = self.oauthSignatureForMethod("POST", url: NSURL(string:self.access_token_url)!, parameters: param)
        
        // アルファベット順に並べ替える
        var authorizationParameterComponents = urlEncodedQueryStringWithEncoding(param).componentsSeparatedByString("&") as [String]
        authorizationParameterComponents.sort { $0 < $1 }
        
        // リクエスト文字列作成
        var headerComponents = [String]()
        for component in authorizationParameterComponents {
            let subcomponent = component.componentsSeparatedByString("=") as [String]
            if subcomponent.count == 2 {
                headerComponents.append("\(subcomponent[0])=\"\(subcomponent[1])\"")
            }
        }
        
        // request
        var request : NSMutableURLRequest = NSMutableURLRequest(URL: NSURL(string: self.access_token_url)!);
        request.HTTPMethod = "POST"
        request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
        
        // カスタムヘッダ設定
        request.setValue("OAuth " + ", ".join(headerComponents), forHTTPHeaderField: "Authorization")
        
        // 非同期通信開始
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
            response, data, error in
            
            if(error != nil){
                // エラー文言表示
                println(error.description)
            }
            // oauth_token表示
            print(NSString(data: data, encoding: self.dataEncoding)!)

        }

    }

上記により、access_token(パラメータ上はoauth_token, oauth_token_secret)を取得できます。
リクエストトークンの取得と差異があったのは先ほど取得したパラメータを使用するというところのみになります。

ここで取得したoauth_token, oauth_token_secretを使用して、TwitterAPIにアクセスします。

home_timelineを取得する

Twitter OAuth v1.1になって毎回カスタムヘッダを作成しなければ情報を取得できなくなってしまったみたいですね。
ということでタイムラインを取得するAPIを使用して取得してみましょう。

    // タイムライン取得
    func getUserTimeline(tokenParam: String) -> Void{
        var param = Dictionary<String, String>()
        var divideParam = tokenParam.componentsSeparatedByString("&")
        for paramStr in divideParam {
            var splitParam = paramStr.componentsSeparatedByString("=")
            if(splitParam[0] == "oauth_token"){
                self.access_token = splitParam[1]
            }else if(splitParam[0] == "oauth_token_secret"){
                self.access_token_secret = splitParam[1]
            }
        }
        
        // 取得に必要なパラメータ
        
        // oauth_tokenが新規に追加される
        param["oauth_token"] = self.access_token
        param["oauth_version"] = "1.0"
        param["oauth_signature_method"] = "HMAC-SHA1"
        param["oauth_consumer_key"] = self.consumer_key
        param["oauth_timestamp"] = String(Int64(NSDate().timeIntervalSince1970))
        param["oauth_nonce"] = (NSUUID().UUIDString as NSString).substringToIndex(8)
        param["oauth_signature"] = self.oauthSignatureForMethod("GET", url: NSURL(string: "https://api.twitter.com/1.1/statuses/home_timeline.json")!, parameters: param)
        
        
        // http header用パラメータ作成
        // アルファベット順に並べ替える
        var authorizationParameterComponents = urlEncodedQueryStringWithEncoding(param).componentsSeparatedByString("&") as [String]
        authorizationParameterComponents.sort { $0 < $1 }
        
        // リクエスト文字列作成
        var headerComponents = [String]()
        for component in authorizationParameterComponents {
            let subcomponent = component.componentsSeparatedByString("=") as [String]
            if subcomponent.count == 2 {
                headerComponents.append("\(subcomponent[0])=\"\(subcomponent[1])\"")
            }
        }
        
        // request
        var request : NSMutableURLRequest = NSMutableURLRequest(URL: NSURL(string: "https://api.twitter.com/1.1/statuses/home_timeline.json")!);
        request.HTTPMethod = "GET"
        request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
        
        // リクエスト設定
        request.setValue("OAuth " + ", ".join(headerComponents), forHTTPHeaderField: "Authorization")
        
        // 非同期通信開始
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
            response, data, error in
            
            if(error != nil){
                // エラー文言表示
                println(error.description)
            }
            // oauth_token表示
            print(NSString(data: data, encoding: self.dataEncoding)!)
            
        }
    }

HMAC-SHA1でパラメータとキーを渡してSHA1ハッシュ値にしてbase64エンコードするところは同じなのですが、ユーザ情報を取得する場合、下記のような変更があります。

// signature作成
    func oauthSignatureForMethod(method: String, url: NSURL, parameters: Dictionary<String, String>) -> String {

        // URLとパラメータをHMAC-SHA1ハッシュ値に変換してbase64エンコードする
        var signingKey : String = "\(self.consumer_secret)&"
        var signingKeyData = signingKey.dataUsingEncoding(dataEncoding)
        
            // access tokenを取得したらkeyにaccess token secretを付加する必要がある
            signingKey = "\(self.consumer_secret)&\(self.access_token_secret)"
            signingKeyData = signingKey.dataUsingEncoding(dataEncoding)
        
        // パラメータ取得してソート
        var parameterComponents = urlEncodedQueryStringWithEncoding(parameters).componentsSeparatedByString("&") as [String]
        parameterComponents.sort { $0 < $1 }
        
        // query string作成
        let parameterString = "&".join(parameterComponents)
        
        // urlエンコード
        let encodedParameterString = urlEncodedStringWithEncoding(parameterString)
        
        let encodedURL = urlEncodedStringWithEncoding(url.absoluteString!)
        
        // signature用ベース文字列作成
        let signatureBaseString = "\(method)&\(encodedURL)&\(encodedParameterString)"
        let signatureBaseStringData = signatureBaseString.dataUsingEncoding(dataEncoding)
        
        // signature作成
        return SHA1DigestWithKey(signatureBaseString, key: signingKey).base64EncodedStringWithOptions(nil)
    }

consumer_key_secretだけでなく取得したoaut_token_secretも連結してキーに渡して証明書を作成する必要があります。
ここで引っかかって全然取得できなかったのは良い思い出です。。。

成功すると下記のようなjsonが返ってきます。

[
    {
        "contributors": null, 
        "coordinates": null, 
        "created_at": "Sun Feb 15 09:09:26 +0000 2015", 
        "entities": {
            "hashtags": [], 
            "media": [
                {
                    "display_url": "pic.twitter.com/Jsu0oYxcGK", 
                    "expanded_url": "http://twitter.com/NIKEiD_JP/status/566884628070682624/photo/1", 
                    "id": 566884626841747456, 
                    "id_str": "566884626841747456", 
                    "indices": [
                        139, 
                        140
                    ], 
......
                }
            ], 
// 長いので省略 
            "favourites_count": 0, 
            "follow_request_sent": false, 
            "followers_count": 169270, 
            "following": true, 
            "friends_count": 15, 
            "geo_enabled": false, 
            "id": 1946049950, 
            "id_str": "1946049950", 
            "is_translation_enabled": false, 
            "is_translator": false, 
            "lang": "ja", 
            "listed_count": 483, 
            "location": "NIKE", 
            "name": "NikeStoreJapan", 
            "notifications": false, 
            "profile_background_color": "131516", 
            "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/378800000106403467/c19a3d26691f9eb4bb431ad3f87676ad.jpeg", 
            "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/378800000106403467/c19a3d26691f9eb4bb431ad3f87676ad.jpeg", 
            "profile_background_tile": true, 
            "profile_banner_url": "https://pbs.twimg.com/profile_banners/1946049950/1418890257", 
            "profile_image_url": "http://pbs.twimg.com/profile_images/472164680534732800/brtYH1Lj_normal.jpeg", 
            "profile_image_url_https": "https://pbs.twimg.com/profile_images/472164680534732800/brtYH1Lj_normal.jpeg", 
            "profile_link_color": "009999", 
            "profile_location": null, 
            "profile_sidebar_border_color": "FFFFFF", 
            "profile_sidebar_fill_color": "EFEFEF", 
            "profile_text_color": "333333", 
            "profile_use_background_image": false, 
            "protected": false, 
            "screen_name": "NikeStoreJapan", 
            "statuses_count": 1393, 
            "time_zone": "Tokyo", 
            "url": "http://t.co/nKcfAyJlq0", 
            "utc_offset": 32400, 
            "verified": true
        }
    }
]

成功しましたね!
あとはjsonをパースして表示するだけです。

終わりに

一応home_timelineは取得できましたが、また次の壁に出会いました。
カスタムヘッダではなくAPIのパラメータを付加して何かする(つぶやくなど)ことがまだ実現できていません。
そのほか色々やることがまだまだある予感です。

  • safariじゃなくて WebViewで認証画面出してもろもろ操作したい
  • つぶやくなどパラメータを付加してデータ取得したい
  • そもそもタイムライン並べてない(jsonパーサ作ろう)
  • ソースコードももっと汎用性を持たせてライブラリ化しておきたい

などなど・・・
先は長そうですね・・・・

でも認証周りが成功したということはほぼ終わったも同然だと考えていますので、
さくさく進められたらいいなと思う今日この頃です!

SwiftでTwitter OAuthを取得する oauth_token編

Swiftやらプログラミングのモチベが結構高まってきてます。
以前から苦戦していたTwitter OAuth認証Swiftでやりたいと思ってずっと試行錯誤してました。
今日やっとoatuh_tokenを取得できたので備忘録として残します。

Twitterアプリケーションの作成

https://dev.twitter.com/
ここのTOOLS -> Manage Your Appsへリンク辿ってクライアントを作成します。
必要なものはTwitterアカウントで、今から作成する場合は電話番号を登録しないと作成できないみたいです。

あとはCreateNewAppのボタンから必要な情報を入力して
クライアントを作成します。

callbackのアドレスを入力する欄があるのですが、
この欄に何かしら値を入れておかないと認証できないとか。
ですので今回はとりあえず自分のtwitterページを登録しておく・・・
カスタムURLスキーマを設定しておけばそこを無視してくれるので特に問題ないはず。

OAuth認証

OAuth認証がいまいち理解できなかったのでメモ。
ちゃんと後から勉強して正しい情報載せたいと思うけど、
とりまTwitterの認証フローを参考にまとめてみる。

クライアントを登録するとConsumer KeyとConsumer Secretの2つが発行される。
これを使ってoauth_tokenを取得し、access_tokenを取得してAPIにアクセスする。

request発行 -> oauth_token取得 -> access_token取得 -> apiアクセス
こんな流れ。
詳しいことはちゃんともろもろ取得してからその際に・・・

oauth_token取得

access_tokenを取得するためのoauth_token, oauth_token_secretを取得するプログラムを書いた。
画面側はとりあえずButtonと書かれたボタンをクリックするとトークンを取得するようにしてる。
最初のテンプレートはSingle View Application。

ViewController.swift

import UIKit
import Foundation

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
    }

    @IBAction func clickButton(sender: AnyObject) {
        doTwitter()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // Twitter OAuth認証
    func doTwitter(){
        let oauth = OAuthSwift(
            consumerKey:    Twitter["consumerKey"]!,
            consumerSecret: Twitter["consumerSecret"]!,
            requestTokenUrl: "https://api.twitter.com/oauth/request_token",
            authorizeUrl:    "https://api.twitter.com/oauth/authorize",
            accessTokenUrl:  "https://api.twitter.com/oauth/access_token"
        )
        
        // 通信開始
        oauth.start()
    }
}

OAuthSwift.swfit

import Foundation

class OAuthSwift{
    // oauthリクエスト参考
    // http://developer.yahoo.co.jp/other/oauth/signinrequest.html
    // http://www.pressmantech.com/tech/programming/1137
    
    
    var dataEncoding: NSStringEncoding = NSUTF8StringEncoding
    
    var data : NSMutableData? = nil
    
    var consumer_key: String
    var consumer_secret: String
    var request_token_url: String
    var authorize_url: String
    var access_token_url: String
    
    // コンストラクタ
    init(consumerKey: String, consumerSecret: String, requestTokenUrl: String, authorizeUrl: String, accessTokenUrl: String){
        self.consumer_key = consumerKey
        self.consumer_secret = consumerSecret
        self.request_token_url = requestTokenUrl
        self.authorize_url = authorizeUrl
        self.access_token_url = accessTokenUrl
    }
    
    func start() -> Void{
        // クライアントアプリケーションの作成
        // 流れとして request_token投げる -> ouath_token取得(今ここ) -> authrize_request投げる -> access_token取得 -> API使用

        // リクエストURL設定
        let twitterURL : NSURL = NSURL(string: "https://api.twitter.com/oauth/request_token")!

        // request
        var request : NSMutableURLRequest = NSMutableURLRequest(URL: twitterURL);
        request.HTTPMethod = "POST"
        request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData

        // oauth_request設定
        var param = Dictionary<String, String>()
        // バージョン
        param["oauth_version"] = "1.0"
        // 証明書アルゴリズム(Twitterでは固定)
        param["oauth_signature_method"] = "HMAC-SHA1"
        // ConsumerKey
        param["oauth_consumer_key"] = self.consumer_key
        // Unixタイムスタンプ
        param["oauth_timestamp"] = String(Int64(NSDate().timeIntervalSince1970))
        // ランダムな文字列
        param["oauth_nonce"] = (NSUUID().UUIDString as NSString).substringToIndex(8)
        // コールバック
        param["oauth_callback"] = "oauth-swift://"
        // 証明書
        param["oauth_signature"] = self.oauthSignatureForMethod("POST", url: twitterURL, parameters: param)
        
        // アルファベット順に並べ替える
        var authorizationParameterComponents = urlEncodedQueryStringWithEncoding(param).componentsSeparatedByString("&") as [String]
        authorizationParameterComponents.sort { $0 < $1 }

        // リクエスト文字列作成
        var headerComponents = [String]()
        for component in authorizationParameterComponents {
            let subcomponent = component.componentsSeparatedByString("=") as [String]
            if subcomponent.count == 2 {
                headerComponents.append("\(subcomponent[0])=\"\(subcomponent[1])\"")
            }
        }
        
        
        // 最終的にリクエストするのは下記文字列
        // "OAuth oauth_callback=\"swift-oauth%3A%2F%2Fswift-oauth%2F\", oauth_consumer_key=\"CXubzXLR2vzqbCf1d9maSJ4ob\", oauth_nonce=\"46B623F4\", oauth_signature=\"%2Brx8F1ofGHhQe0iN%2Ff7MArz05F4%3D\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1422107013\", oauth_version=\"1.0\""
        
        // リクエスト設定
        request.setValue("OAuth " + ", ".join(headerComponents), forHTTPHeaderField: "Authorization")

        // 非同期通信開始
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
            
            response, data, error in
            
            if(error != nil){
                // エラー文言表示
                println(error.description)
            }
            // oauth_token表示
             print(NSString(data: data, encoding: self.dataEncoding)!)
        }
    }
    
    // サーバからレスポンスを受け取ったときのデリゲート
    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
        // Recieved a new request, clear out the data object
        self.data! = NSMutableData()
    }
    
    // サーバからデータが送られてきたときのデリゲート
    func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
        self.data!.appendData(data)
    }
    
    // データロードが完了したときのデリゲート
    func connectionDidFinishLoading(connection: NSURLConnection!){
        // バイナリデータが発行される
        let html : String = NSString(data: self.data!, encoding: NSUTF8StringEncoding)!
        // コンソールに出力
        println(html)
        
    }
    
    // signature作成
    func oauthSignatureForMethod(method: String, url: NSURL, parameters: Dictionary<String, String>) -> String {
        let signingKey : String = "\(self.consumer_secret)&"
        let signingKeyData = signingKey.dataUsingEncoding(dataEncoding)
        
        // パラメータ取得してソート
        var parameterComponents = urlEncodedQueryStringWithEncoding(parameters).componentsSeparatedByString("&") as [String]
        parameterComponents.sort { $0 < $1 }
        
        // query string作成
        let parameterString = "&".join(parameterComponents)
        
        // urlエンコード
        let encodedParameterString = urlEncodedStringWithEncoding(parameterString)
        
        let encodedURL = urlEncodedStringWithEncoding(url.absoluteString!)
        
        // signature用ベース文字列作成
        let signatureBaseString = "\(method)&\(encodedURL)&\(encodedParameterString)"
        let signatureBaseStringData = signatureBaseString.dataUsingEncoding(dataEncoding)
        
        // signature作成
        return SHA1DigestWithKey(signatureBaseString, key: signingKey).base64EncodedStringWithOptions(nil)
    }

    // Dictionary内のデータをエンコード
    func urlEncodedQueryStringWithEncoding(params:Dictionary<String, String>) -> String {
        var parts = [String]()
        
        for (key, value) in params {
            let keyString = urlEncodedStringWithEncoding(key)
            let valueString = urlEncodedStringWithEncoding(value)
            let query = "\(keyString)=\(valueString)" as String
            parts.append(query)
        }
        
        return "&".join(parts) as String
    }

    // URLエンコード
    func urlEncodedStringWithEncoding(str: String) -> String {
        let charactersToBeEscaped = ":/?&=;+!@#$()',*" as CFStringRef
        let charactersToLeaveUnescaped = "[]." as CFStringRef
        
        var raw: NSString = str
        
        let result = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, raw, charactersToLeaveUnescaped, charactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(dataEncoding)) as NSString
        
        return result
    }
    
    // SHA1署名のハッシュ値を作成
    func SHA1DigestWithKey(base: String, key: String) -> NSData {
        let str = base.cStringUsingEncoding(dataEncoding)
        let strLen = UInt(base.lengthOfBytesUsingEncoding(dataEncoding))
        let digestLen = Int(CC_SHA1_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
        let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding)
        let keyLen = UInt(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
        
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), keyStr!, keyLen, str!, strLen, result)
        
        return NSData(bytes: result, length: digestLen)
    }
}

はい、https://api.twitter.com/oauth/request_token にPOSTでリクエスト送るだけ。
ちょっと制限があって、query stringのパラメータ名が昇順じゃないといけないとかハッシュ値作成しなきゃいけないとか。
その辺はプログラムソース見てもらえれば、全部できたら改めて書き直すと思う。

これから

ライブラリ使えばこの辺一発でできるんだけどそれだと理解できないから作っただけ、自己満足。
とりあえずツイート取得、投稿、その他昨日を提供できるようにすることとOAuthTwitterを綺麗にしたりHTTPClient作ったりすることを進めていく。
あとは忘れないようにちゃんとこの辺の知見のまとめをこのブログに残しておく。

最低限これをやっていこうと思う。
近いうちにまずはツイート取得を目指していこう!