クロスドメインでSalesforceにアクセス

by Shinichi Tomita on 10月 17, 2007 at 08:51 午後

今回の記事はSalesforceプラットフォームの開発という点ではちょっと細部のお話になるかもしれませんが、よろしければご覧ください。

一般的に、インターネット上に散在している様々なWebサービスを気軽に利用するということを考えたとき、Webサイトをまたがって(クロスドメインで)外部のWebサービスにアクセスできるということは大変重要になります。しかしながら現在のWebブラウザでは、クロスドメインでのリクエスト送信が様々な理由により制限されています。多くはセキュリティ上の理由によるものですが、このためにWebサービスを中継するためのサーバプログラムが必要になるなどして、気軽なマッシュアップの可能性が制限されているというのも事実です。

以前にも記事として書きましたが、Salesforceではこのクロスドメインの制限を回避するために、AJAX Proxy といった仕組みを提供することでSalesforce開発者に対して外部のWebサービスへのアクセスを簡単に行えるようにしています。しかしながら、反対に外部のWebサイトからSalesforceのWebサービスを使うという時には、今までこれといった有効な手段はアナウンスされていませんでした。

実は、Adobe Flash と Salesforceの提供する Flex Toolkit for Force.com とを組み合わせて使うことで、外部WebサイトからのクロスドメインでのSalesforceへのアクセスが可能になっています。

Adobe Flashでは、クロスドメインリクエストをcrossdomain.xmlというポリシーファイルを配置することによって実現する仕組みを提供しています。つまり、呼び出されるWebサービスがこのポリシーファイルを提供しており、呼び出し側のWebサイトがその条件を満たしているのであれば、サービスに対してクロスドメインでアクセスができるようになります。

ただし、実際にポリシーファイルを配置するかどうかについてはWebサービス側に任されているため、FlashからであればすべてのWebサービスにアクセスできるわけではありませんが、Salesforceではこのcrossdomain.xmlポリシーファイルをディレクトリに配置しているため、Flash(Flex)クライアントから簡単にクロスドメインでのアクセスができるようになっています。

なお、通常であれば crossdomain.xmlをサーバルート(/)においておくだけでFlashクライアントは自動的にポリシーを解決してくれるのですが、Salesforceではセキュリティ上の理由によりポリシーファイルをサーバルートには置いていません。ポリシーファイルはプログラムから明示的にポリシーファイルを読み出すことで指定します。

Salesforceのクロスドメインポリシーファイルは以下のURLで提供されます。

http://www.salesforce.com/services/crossdomain.xml

サンプル(ActionScript 3)
package {
  import flash.display.Sprite;
  import flash.system.Security;
  import flash.text.TextField;
  import com.salesforce.*;
  import com.salesforce.events.*;
  import com.salesforce.objects.*;
  import com.salesforce.results.*;

  public class SforceLogin extends Sprite {

    private var conn:Connection = new Connection();

    public function SforceLogin() {
      login('username@example.com', 'mypassword');
    }

    private function login(username:String, password:String):void {
      // ロードするポリシーファイルを明示的に指定する
      Security.loadPolicyFile('http://www.salesforce.com/services/crossdomain.xml');

      // 非SSLのエンドポイントに変更。WebサイトがSSLの場合は必要なし
      conn.serverUrl = 'http://www.salesforce.com/services/Soap/u/10.0';
      conn.login(new LoginRequest({
        username : username,
        password : password,
        callback : new AsyncResponder(handleLogin)
      }));
    }

    private function handleLogin(result:LoginResult):void {
      // ログインしたユーザのユーザ情報からフルネームを表示
      var textField:TextField = new TextField();
      textField.text = "Hello " + result.userInfo.userFullName;
      addChild(textField);
    }

  }
}

またSalesforceでは、ログイン後(セッションを取得した後)にはWebサービスのエンドポイントURLが変更になり、リクエストを送信するサーバが変わります。そのため別途もう一つのクロスドメインポリシーファイルをロードする必要があります。

ログイン後のクロスドメインポリシーファイルのURL

http://instance.salesforce.com/services/Soap/u/cross-domain.xml

instance にはna1,na2,apなどのサーバ名が入りますが、これはログインするユーザごとにそれぞれ異なるものです。そのため、あらかじめこの情報をコード内に埋め込むことは避けるべきです。ユーザがどのサーバに割り当てられているかについては、ログイン後の情報として取得することができます。

private function handleLogin(lr:LoginResult):void {
  // 接続先のサーバURLを強制的に非SSLに変更
  conn.serverUrl = lr.serverUrl.replace(/^https/, 'http');

  // サーバURLからのルートURLのみを切り出す(e.g. http://na1.salesforce.com)
  var policyUrl:String = conn.serverUrl.match(/^http:\/\/[^\/]+/)[0];
  policyUrl += '/services/Soap/u/cross-domain.xml';

  Security.loadPolicyFile(policyUrl);

ちなみにSSL環境下に作成されたSWFファイルを配置し、SSL接続でAPIにリクエストを行う場合には、このようなポリシーファイルのロード作業はすべてFlex Toolkitが行ってくれます。非SSLサイトからは非SSLのAPIエンドポイントに対してのみ接続可能なので、実際にSWFファイルを配置する場合はできるだけSSLで提供されるWebページ中に配置したほうがよいでしょう。

以上の仕組みを踏まえて、クロスドメインでSalesforceの情報を取得して表示するFlashウィジットを公開したいと思います。このウィジットはSalesforceに登録されている取引先情報の一覧を表示します。Salesforceへのログインアカウントがない方は以下のログインアカウントで試してみてください。もちろん言ってみればただのFlashのムービーですので、HTMLが記述できるサイトであればどこでも簡単に埋め込むことができます(なお再生にはFlash Player 9以上が必要です)

Salesforce Flex ウィジット

ソース(Flex MXMLファイル)
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:salesforce="com.salesforce.*"
                layout="vertical" width="250" height="300"
                paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0">
  <mx:Script>
    <![CDATA[
import flash.system.Security;
import mx.collections.ArrayCollection;
import com.salesforce.*;
import com.salesforce.events.*;
import com.salesforce.objects.*;
import com.salesforce.results.*;

[Bindable]
private var accounts:ArrayCollection;

private function login():void {
  // ポリシーファイルのロード
  Security.loadPolicyFile('http://www.salesforce.com/services/crossdomain.xml');

  var lr:LoginRequest = new LoginRequest({
    username : username.text,
    password : password.text,
    callback : new AsyncResponder(handleLogin)
  });
  conn.login(lr);
}

private function handleLogin(lr:LoginResult):void {
  // 接続先のサーバURLを強制的に非SSLに変更
  conn.serverUrl = lr.serverUrl.replace(/^https/, 'http');
  // サーバURLからのルートURLのみを切り出す(e.g. http://na1.salesforce.com)
  var policyUrl:String = conn.serverUrl.match(/^http:\/\/[^\/]+/)[0];
  policyUrl += '/services/Soap/u/cross-domain.xml';
  Security.loadPolicyFile(policyUrl);

  queryAccounts();
}

private function queryAccounts():void {
  var soql:String = "SELECT Id, Name, BillingState FROM Account LIMIT 100";
  conn.query(soql, new AsyncResponder(handleQueryAccountResult));
}

private function handleQueryAccountResult(result:QueryResult):void {
  accounts = result.records;
  mainViewStack.selectedIndex = 1; // Account Viewを前面に
}
    ]]>
  </mx:Script>

  <salesforce:Connection id="conn" serverUrl="http://www.salesforce.com/services/Soap/u/10.0" />

  <mx:ViewStack id="mainViewStack" width="100%" height="100%">
    <mx:Canvas width="100%" height="100%">
      <mx:Panel id="loginPanel" height="100%" layout="vertical" width="100%"
                title="Salesforce ログイン" horizontalAlign="center" verticalAlign="middle" x="0" y="0">
        <mx:Form width="100%" height="100%">
          <mx:FormItem label="ユーザ名" width="100%">
            <mx:TextInput id="username" width="100%" text="stomita@main.dev"/>
          </mx:FormItem>
          <mx:FormItem label="パスワード" width="100%">
            <mx:TextInput id="password" displayAsPassword="true" width="100%" text="e10rider"/>
          </mx:FormItem>
          <mx:FormItem width="100%">
            <mx:Button label="Login" labelPlacement="left" click="login()"/>
          </mx:FormItem>
        </mx:Form>
      </mx:Panel>
    </mx:Canvas>
    <mx:Canvas width="100%" height="100%">
      <mx:Panel id="accountPanel" height="100%" layout="vertical" width="100%"
                title="取引先リスト" horizontalAlign="center" verticalAlign="middle" x="0" y="0">
        <mx:DataGrid dataProvider="{accounts}" width="100%" height="100%">
          <mx:columns>
            <mx:DataGridColumn dataField="Name" headerText="取引先名"/>
            <mx:DataGridColumn dataField="BillingState" headerText="都道府県" />
          </mx:columns>
        </mx:DataGrid>
      </mx:Panel>
    </mx:Canvas>
  </mx:ViewStack>

</mx:Application>

上記記載のActionScript、およびMXMLファイルのコンパイル方法については、Flexを使ったApexアプリケーション開発をご覧ください。

トラックバック

このページのトラックバックURL: http://www.typepad.jp/t/trackback/7240/10496213

このページへのトラックバック一覧 クロスドメインでSalesforceにアクセス:

コメントを投稿

コメントは記事の投稿者が承認するまで表示されません。