しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET MVC の Facebook テンプレートを使って Graph API を便利に使う

ASP.NET MVC の Facebook テンプレートは Facebook SDK for .NET をベースに、色々とアプリを作る上で便利そうな機能*1が実装されています。

signed_request のデコードや認証周りを自動でやってくれるんですが、FacebookClient クラスに対する拡張メソッドやクラスが割と充実しています。それらを使うと Graph API を簡単に叩けるようになるので、これが想像以上に便利なんです。

まずは基本的な使い方から紹介します。例えば、自分の情報を取得する場合には以下のように書きます。

[FacebookAuthorize]
public Task<ActionResult> Index(FacebookContext context)
{
    if (ModelState.IsValid)
    {
        // dynamic で受けるとそのまま使える
        dynamic user = await context.Client.GetCurrentUserAsync();
    }

    return View();
}

GetCurrentUserAsync というのが Facebook テンプレートで追加された拡張メソッドで、実体は以下のようなコードです。

// GetFacebookObjectAsync<T> は FacebookClient に対する拡張メソッド
dynamic user = await context.Client.GetFacebookObjectAsync<object>("me");

Graph API のパスを良い感じに隠してくれています。この場合だとあんまりメリットを感じないかもしれないですが、後で GetFacebookObjectAsync 拡張メソッドはかなり強力なことが分かると思います。

ちなみに、ほぼ全てのメソッドが TAP に対応しているので async と await を使って、非同期処理を使わないのがおかしいと思えるほど簡単に書けます。実に素晴らしい。

ちなみに dynamic で受け取っていますが、ジェネリックなメソッドも用意されているので、クラスを用意すればデシリアライズも可能です。実際に Facebook テンプレートでは MyAppUser というクラスへマッピングを行っています。

// JSON を MyAppUser クラスへデシリアライズする
var user = await context.Client.GetCurrentUserAsync<MyAppUser>();

そして MyAppUser クラスの定義は以下のような感じです。

プロパティ名がそのまま Graph API での fields に指定される値になるので、詳しい情報はリファレンス User - Facebook開発者 を参照してもらえればと思います。

public class MyAppUser
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }

    [JsonProperty("picture")]
    [FacebookFieldModifier("type(large)")]
    public FacebookConnection<FacebookPicture> ProfilePicture { get; set; }

    [FacebookFieldModifier("limit(8)")]
    public FacebookGroupConnection<MyAppUserFriend> Friends { get; set; }

    [FacebookFieldModifier("limit(16)")]
    public FacebookGroupConnection<FacebookPhoto> Photos { get; set; }
}

Facebook テンプレートではデフォルトで Json.NET を使ってデシリアライズを行っているので、JsonProperty 属性を使って別名を指定できます。

それはともかくとして、新しく FacebookFieldModifier 属性というものが出てきました。いろいろと説明は眠いので省きますが、FacebookFieldModifier 属性を指定しておくと GetFacebookObjectAsync を使った場合に、取得したい情報を制御できるようになります。この場合だとプロフィール写真は大きいサイズが、友達は 8 人まで、写真は 16 枚までといった感じに取得できます。*2

FacebookConnection と FacebookGroupConnection はリファレンスの Connections セクションに記載されている情報を取得する時に使います。違いは値が一つか、複数かというだけです。ダラダラと説明が続くと飽きるので、実際にいろんな情報を取得してみます。

友達一覧を取得する

Facebook テンプレートで拡張メソッドが用意されているので、そのメソッドを呼び出すだけで簡単に取得できます。

// dynamic の場合 friends.data に配列で情報が格納されている
dynamic friends = await context.Clients.GetCurrentUserFriendsAsync();

dynamic で受け取る場合、これが想像以上に扱いがめんどくさいので、個人的には以下のようにクラスへデシリアライズするのをおすすめします。

// クラスへデシリアライズの場合、コレクションが返ってくる
var friends = await context.Client.GetCurrentUserFriendsAsync<FacebookFriend>();

この場合では friends はコレクションになるので、非常に簡単に扱えるようになっています。

パーミッション、ステータス、写真を取得する

これら 3 つは友達一覧と同じような拡張メソッドが用意されているので、特に何も考えなくても使えます。

// パーミッションを取得する
var permissions = await context.Client.GetCurrentUserPermissionsAsync();

// ステータス(アップデート)を取得する
var statuses = await context.Client.GetCurrentUserStatusesAsync<FacebookStatus>();

// 写真を取得する
var photos = await context.Client.GetCurrentUserPhotosAsync<FacebookPhoto>();

FacebookStatus と FacebookPhoto は単純なクラスなので省略します。

拡張メソッドが用意されていると簡単に使えるので非常に便利ですが、用意されていない Graph API の情報を取得することもちゃんと出来るので、次で具体的なコードを紹介しておきます。

アルバム一覧を取得する

自分のアルバム一覧は Graph API の me/albums で取得することが出来ます。そして、以下のようなクラスを用意しておきます。

public class FacebookAlbum
{
    public string Id { get; set; }

    public string Name { get; set; }
}

実際にアルバム一覧は、以下のようにメソッドを呼び出せば取得できます。

var albums = await context.Client.GetFacebookObjectAsync<FacebookGroupConnection<FacebookAlbum>>("me/albums");

ちょっと冗長だと思う場合には、以下のような拡張メソッドを用意することも出来ますね。

public static async Task<IList<TUserAlbum>> GetCurrentUserAlbumsAsync<TUserAlbum>(this FacebookClient client) where TUserAlbum : class
{
    var  albums= await GetFacebookObjectAsync<FacebookGroupConnection<TUserAlbum>>(client, "me/albums");

    return albums != null ? albums.Data : new TUserAlbum[0];
}

アクセストークンの期限を延長する

実は今までの流れとあまり関係ないのですが、Facebook の signed_request や code から取得できるアクセストークンは 2 時間ぐらいで切れてしまう時があるので、60 日有効なアクセストークンを取得しておかないと悩むことになります。

という訳で、以下のようなコードで取得してやります。

public static async Task<string> ExchangeAccessToken(string accessToken)
{
    var client = new FacebookClient();

    dynamic result = await client.GetTaskAsync("oauth/access_token", new
    {
        client_id = AppSettings.Facebook.AppId,
        client_secret = AppSettings.Facebook.AppSecret,
        grant_type = "fb_exchange_token",
        fb_exchange_token = accessToken
    });

    return result.access_token;
}

AppId や AppSecret は拙作の NuGet Gallery | SwissKnife.T4.AppSettings 0.0.4 を使って Web.config からクラスを自動生成しています。*3

まとめ

もうちょっと拡張メソッドが用意されていてほしいとか、そもそもページタブやモバイル Web アプリを作る時にめんどくさくてイケてないとか、まあいろいろと思うんですが、Graph API を素の Facebook SDK より使いやすくしてくれているのが良いです。

特に FacebookFieldModifier 属性を使って、画像サイズを指定したり件数を指定するのはかなり便利なので、ASP.NET MVC 5 で進化するのも期待しつつ使ってみるのも良いかなと思います。

*1:実際に便利に使えるかはどうだろう?

*2:詳しくは Graph API のリファレンスを参照のこと

*3:ステマ