Cutting Edge

基本的な Facebook プログラミング: Windows クライアントを構築する

Dino Esposito

コード サンプルをダウンロードする

Dino Esposito前回のコラム (msdn.microsoft.com/magazine/jj863128、英語) では、Web サイト (具体的には ASP.NET MVC Web サイト) のコンテキストでの Facebook プログラミングの基本について説明しました。Facebook API が最もよく使われる用途の 1 つが、"より柔軟" な方法で新しい Web サイトのユーザーを認証することにすぎないとしても、ご想像どおり、Facebook API の用途は Web アプリに限定されません。

今回は Windows Presentation Foundation (WPF) クライアント アプリを構築します。このアプリでは、Facebook API を使用してユーザーを認証し、画像を添えて近況を投稿します。Web とデスクトップは環境が異なるため、前回のコラムとは明らかに異なります。ただし、Facebook API を使用するのは同じです。Facebook C# SDK は NuGet から入手できます。C#/.NET 開発者向けの Facebook API の詳細については、facebookapi.codeplex.com (英語) を参照してください。

アプリとソーシャル ネットワークを統合する

大部分のソーシャル ネットワークのアーキテクチャは似ています。ユーザーとソーシャル ネットワーク エンジンが対話する方法は 2 つあり、メイン Web サイト (Twitter サイトや Facebook サイトなど) から直接対話するか、ソーシャル ネットワークのサイトが内部でホストしているアプリの仲介を受けて対話します。わかりやすいように、ここではこのようなアプリをコネクタと呼びます。

クライアント アプリ (Web サイト、WPF、または Windows Phone) は、コネクタの仲介がないとソーシャル ネットワーク エンジンと対話できません。コラム付属のサンプル コードでは、Memento という Facebook コネクタを使用します (例として示すサンプル コードは単一プロジェクトで、完全なソリューションではありません。また、Visual Studio 2010 を使用しています)。Memento を使用して Facebook に接続するクライアント アプリによって行われたウォール投稿のフッターには、Memento という名前と関連するアイコンが表示されます。Facebook のコネクタは developers.facebook.com/apps (英語) で作成できます。

コネクタは重複しない文字列のペア (アプリ キーとシークレット) によって特性が決まり、今回の目的にはさらに興味深いことですが、Web、モバイル、デスクトップなどさまざまなクライアント アプリにサービスを提供できます。図 1 は、対話に関するソーシャル ネットワークの全体的なアーキテクチャをまとめたものです。

The Interaction Model of Social Networks
図 1 ソーシャル ネットワークの対話モデル

つまり、最も普及しているソーシャル ネットワーク (Facebook、Twitter、Foursquare など) との統合を必要とするあらゆるユーザー アプリは、ソーシャル ネットワーク固有のコネクタ アプリのクライアントとして構築することが不可欠です。

ソーシャル ネットワークに統合するアプリは、基本的には、登録済みのユーザーに代ってソーシャル ネットワークの機能にアクセスします。このようなアクセスには、ソーシャル ネットワークに対するユーザー認証というアクションと、特定のコミュニティへのチェックイン、ユーザーのウォールやツイートへの投稿といった実際のアクションの 2 つが必要になります。

このことは、ソーシャル ネットワーク API をカスタム アプリに統合する際の基本で、Web アプリに限られることではありません。

Windows アプリとの違い

前回は、最初に Facebook で Web サイトのユーザーを認証する方法を説明し、ユーザーのウォールへの投稿を、対話で行うコードとプログラムで行うコードをいくつか紹介しました。Windows アプリからユーザーのウォールに投稿するアプローチに変わりはありませんが、認証にはいくつか調整が必要です。

ソーシャル ネットワークは基本的には Web アプリで、OAuth プロトコルを使って認証サービスを公開します。Facebook アカウントを使ってユーザーを認証するアプリは、たとえば、なんらかの Facebook エンドポイントに HTTP 呼び出しを行う必要があります。ただし、このエンドポイントは、実際のユーザーに対して、資格情報を入力し、コネクタがそのユーザーに代わって操作することを承認するための対話を行う HTML ページを返します (図 2 参照)。

The Memento Connector Explicitly Asks the User for Permissions
図 2 Memento コネクタはユーザーにアクセス許可を明示的に問い合わせる

Windows シナリオやモバイル シナリオが Web アプリと異なる点は、資格情報とアクセス許可のためにソーシャル ネットワークが提供する HTML ページへのリダイレクトを処理する方法です。

さらにもう 1 つの Facebook Windows クライアント

では、WPF プロジェクトを作成し、メイン ウィンドウに少し XAML マークアップを追加しましょう。最低でも、ログイン処理とログアウト処理を実行する 2 つのボタンと、現在ログインしているユーザー名を表示するラベルが必要です。また、現在のユーザーの画像を表示する image 要素を含めてもかまいません。他には何が必要でしょう。Login クリック ハンドラーに含めた次のコードをご覧ください。

var loginUrl = FbHelpers.GetLoginUrl();

まず、Facebook からログイン URL を取得しています。この URL を取得するコードは Web シナリオで使用したのと同じです (図 3 参照)。

図 3 ログイン URL を取得するコード

public static String GetLoginUrl()
{
  var client = new FacebookClient();
  var fbLoginUri = client.GetLoginUrl(new
  {
    client_id = ConfigurationManager.AppSettings["fb_key"],
    redirect_uri =
    "https://www.facebook.com/connect/login_success.html", 
    response_type = "code",
    display = "popup",
    scope = "email,publish_stream"
  });
  return fbLoginUri.ToString();     
}

お気づきかもしれませんが、redirect_uri パラメーター (必須) は Facebook の success エンドポイントを指しています。このエンドポイントは、ログイン処理終了後のランディング ページです。Web シナリオでは現在のページを使用するため、ユーザーは最初に Facebook サイトにリダイレクトされ、次に元の要求ページに戻ります。

GetLoginUrl メソッドはログイン画面を呼び出すために URL を取得するだけです。Web シナリオでは、単純にリダイレクトを呼び出して新しいページに切り替えて、ログインのための一般的な Facebook の UI を表示するか、(ユーザーが使用中のコンピューターで既にログインしている場合は) 図 2 に示す認証ページを表示します。同じことを Windows アプリで実現するには、最初は非表示の WebBrowser コントロールが UI に必要です。アプリのログイン ボタンのクリック ハンドラーのコード全体を次に示します。

public void Login()
{
  var loginUrl = FbHelpers.GetLoginUrl();
  ShowBrowser = true;
  view.Browser.Navigate(loginUrl);
}

ダウンロードして入手できるサンプル アプリでは、モデル - ビュー - ビューモデル (MVVM: Model-View-ViewModel) パターンを使用して UI の詳細を抽象化しています。したがって、ShowBrowser を true に設定するだけで WebBrowser コントロールが表示されるようになります。最後に、Navigate メソッドにより、コンポーネントを指定した URL にリダイレクトします。図 4 に [ログイン] をクリックする前後のサンプル アプリの状態を示します。

First Step of Login to Facebook
図 4 Facebook にログインする最初の手順

認証処理は最高 2 回の手順になる可能性があります。ユーザーが既にソーシャル ネットワークにログインしている場合、Web ブラウザーは直接ランディング ページを受け取ります。ユーザーがログインしていなければ、図 4 のように表示されます。ただし、使用するコネクタとユーザーがクライアント アプリによって関連付けられていない場合、図 2 で示すように明示的にアクセス許可を求める仲介画面が表示されます。

WebBrowser コンポーネントでのページの読み込みはどのように処理するのでしょう。基本的には、次に示すように Navigated イベント ハンドラーが必要です。

void facebookBrowser_Navigated(Object sender, 
  NavigationEventArgs e)
{
  var fb = new FacebookClient();
  FacebookOAuthResult oauthResult;
  if (!fb.TryParseOAuthCallbackUrl(e.Uri, out oauthResult))
    return;
  if (oauthResult.IsSuccess)           
    _viewPresenter.LoginSucceeded(oauthResult);           
  else           
    _viewPresenter.LoginFailed(oauthResult);
}

Facebook はログイン処理が正常終了したかどうかを示します。コネクタに対して要求されたアクセス許可を単純にユーザーが拒否すると、ログインに失敗します。ログインに失敗した場合は、UI をリセットして Web ブラウザーを非表示にし、ユーザーへのフィードバックを表示します。もっと興味深いのがログインに成功したときです (図 5 参照)。

図 5 ログインの成功時

public void LoginSucceeded(FacebookOAuthResult oauthResult)
{
  // Hide the Web browser
  ShowBrowser = false;
  // Grab the access token (necessary for further operations)
  var token = FbHelpers.GetAccessToken(oauthResult);
  Token = token;
  // Grab user information
  dynamic user = FbHelpers.GetUser(token);
  // Update the user interface
  UserName = String.Format("{0} {1}", user.first_name,
    user.last_name);
  UserPicture = user.picture;
  IsLogged = true;
}

図 5 に示すコードは Web シナリオで使用する必要があったものと同じです。いったんユーザーがログインに成功しても、アプリはまだユーザーに代わって処理を行う準備が整っていません。アプリはユーザーについての情報 (ユーザー名やなんらかの ID さえ) を把握していません。つまり、まだ認証データを保存することができません。したがって、Web シナリオでも Windows シナリオでも、もう 1 つの手順 (アクセス トークンの取得) が不可欠です。

アクセス トークン

アクセス トークンとは、ログイン成功時のコードの応答としてソーシャル ネットワークから返される文字列です。ログイン コードでは、認証を処理したユーザーがネットワークで認識されたことを示すだけです。アクセス コードでは、ユーザー ID とコネクタを結び付けます。アクセス コードは、ユーザー アプリが十分なアクセス許可を持ち、要求された処理をユーザーに代わって実行できるかどうかを、ソーシャル ネットワークのエンジンに伝えます。同じユーザーでもコネクタが異なるとアクセス トークンも異なります。

図 6 に OAuth ログインの成功後にアクセス トークンを取得する C# コードを示します。

図 6 OAuth ログインの成功後にアクセス トークンを取得するコード

public static String GetAccessToken(FacebookOAuthResult oauthResult)
{
  var client = new FacebookClient();
  dynamic result = client.Get("/oauth/access_token",
    new
    {
      client_id = ConfigurationManager.AppSettings["fb_key"],
      client_secret =
      ConfigurationManager.AppSettings["fb_secret"],
      redirect_uri =
      "https://www.facebook.../login_success.html", 
      code = oauthResult.Code
  });
  return result.access_token;
}

Web アプリの場合、アクセス トークンをカスタム cookie に保存するか、カスタム IPrincipal オブジェクトによって管理されるカスタム認証 cookie に追加データとして保持します。Windows シナリオの場合、ローカル ストレージを使用することになります。ユーザーが Windows アプリからログアウトするときに、保存したデータを単純に消去します。特定のユーザーとコネクタのアクセス トークンを保持したら、図 2 で許可されたアクセス許可の下で処理を実行できるようになります。

一般的な種類のソーシャル ネットワーク アプリは、同じユーザーに代って自動的になんらかのフィードや投稿を "リッスン" します。今回の場合、アクセス トークンを取得したら、ユーザーがアクセス許可を取り消すまでアプリの実行を続けることができます。アクセス トークンを保持したら、そのトークンの対象になっているユーザーがコンピューターでソーシャル ネットワークに実際にログインしない限り、認証を処理する必要はありません。

近況や写真を投稿する

図 7 は、ユーザーが Facebook へのログインに成功してアクセス トークンが安全に保存された後のサンプル WPF アプリの UI を示しています。UI には投稿のためのテキスト ボックスとボタンがあります。この UI には、ローカル ディスクを参照して JPEG 画像を選択するボタンもあります。

Posting from the Sample Client App
図 7 サンプル クライアント アプリから投稿する

図 8 のコードでは、添付された画像を添えて近況を投稿する方法を示しています。

図 8 添付画像を添えて近況を投稿するコード

public static void PostWithPhoto(
  String token, String status, String photoPath)
{
  var client = new FacebookClient(token);
  using (var stream = File.OpenRead(photoPath))
  {
    client.Post("me/photos",
    new
    {
      message = status,
       file = new FacebookMediaStream
  {
         ContentType = "image/jpg",
         FileName = Path.GetFileName(photoPath)
        }.SetValue(stream)
    });
  }
}

図 7 では、タイムラインに投稿された近況や画像を確認できます。著者名の下に使用しているコネクタ名が表示されていることに注意してください。

次回: JavaScript

今回はユーザーに代って Facebook に投稿する WPF アプリを構築しました。モバイル アプリを構築する際も、ここで説明したのと同じパターンに従います。どのモバイル プラットフォームを選択しても、このパターンは有効です。これと同じ方法で Windows Phone や Android アプリの作成にも成功しています。次回は JavaScript を使用して Facebook をプログラミングする方法を紹介します。

Dino Esposito は、『Architecting Mobile Solutions for the Enterprise』(Microsoft Press、2012 年) と 『プログラミング Microsoft ASP.NET MVC ASP.NET MVC 3 対応版』(日経BP社、2012 年) の著者であり、『Microsoft .NET: Architecting Applications for the Enterprise』(Microsoft Press、2008 年) の共著者です。Esposito はイタリアに在住し、世界各国で開催される業界のイベントで頻繁に講演しています。Twitter (twitter.com/despos、英語) で彼をフォローしてください。

この記事のレビューに協力してくれた技術スタッフの Scott Densmore に心より感謝いたします。