REST インターフェイスを使用してメッセージを送信するときに、Salesforce で認証する必要があります。 Salesforce でサポートされる REST 呼び出しの認証方法は、WCF-WebHttp アダプターではすぐに使用できません。これは、Salesforce の REST インターフェイスを呼び出すために使用します。 そのため、カスタム WCF エンドポイントの動作を作成し、Salesforce REST インターフェイスを呼び出すように構成 WCF-WebHttp 送信アダプターにアタッチします。
同様に、Salesforce から受信した XML 応答には名前空間は含まれません。 BizTalk Server が応答スキーマに対して応答メッセージを処理するには、応答メッセージにターゲット名前空間を含める必要があります。 そのため、Microsoft BizTalk ESB Toolkit を使用してカスタム パイプラインを作成し、応答メッセージに名前空間を追加します。 次に、このカスタム パイプラインを、要求/応答 WCF-WebHttp 送信ポートの受信パイプラインとして使用します。
Salesforce 認証のカスタム WCF 動作を追加する
Salesforce には、クライアント アプリケーションが Salesforce で認証するためのさまざまなオプションが用意されています。 私たちは Salesforce が「ユーザー名とパスワード」と呼ぶフローを使用します。 クライアント側では、WCF を使用すると、 メッセージの 送信前または受信後にメッセージを変更するメッセージインスペクターを作成できます。 メッセージ インスペクターは、クライアント ランタイムの拡張機能であり、動作として構成されます。 次に、動作拡張要素に動作が追加されます。 この動作拡張要素は、要素名を持つ machine.config に登録され、WCF ポートでカスタム動作として構成できるようになります。 詳細については、「 カスタム動作を使用した WCF の拡張」を参照してください。 このアプローチを使用して、Salesforce で認証するためのユーザー名認証フローを組み込みます。 以下に示す広範な手順は次のとおりです。
Salesforce 認証エンドポイントに対して HTTP POST 呼び出しを行い、アクセス トークンを受信するメッセージ インスペクターを作成します。 次に、メッセージ インスペクターは、Salesforce に送信されるクエリ メッセージの Authorization ヘッダーの値として認証トークンを追加します。 また、メッセージ インスペクターは、受信した応答が XML 形式になるように、要求メッセージに Accept ヘッダーを追加します。 それ以外の場合、Salesforce は既定の JSON 形式でメッセージを送信します。
エンドポイントの動作を作成して、メッセージ インスペクターをクライアント エンドポイントに適用します。
エンドポイントの動作の構成を有効にする動作拡張要素を作成します。
メッセージ インスペクターを作成するには
Visual Studio の BtsSalesforceIntegration ソリューションの一部として、C# クラス ライブラリを作成します。 「
Microsoft.BizTalk.Adapter.Behaviors
」に名前を付け、次の名前空間を追加します。using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; using System.Xml; using System.Net; using System.IO; using System.ServiceModel.Configuration; using System.Configuration; using System.Web.Script.Serialization;
ソリューション エクスプローラーから、
System.ServiceModel
、System.Web.Extensions
、およびSystem.Configuration
への参照を追加します。次のプロパティを使用してクラス
SalesforceOAuthToken
を追加します。 このクラスは、Salesforce から受け取る承認トークンを表します。public class SalesforceOAuthToken { public string id { get; set; } public string issued_at { get; set; } public string refresh_token { get; set; } public string instance_url { get; set; } public string signature { get; set; } public string access_token { get; set; } }
IClientMessageInspector
とIEndpointBehavior
を実装するクラスSalesforceRESTSecurityBehavior
を追加します。 このクラスには、Salesforce 認証エンドポイントへの HTTP POST 呼び出しを行って承認トークンを取得するHttpPost()
メソッドとFetchOAuthToken()
メソッドが含まれます。SalesforceRESTSecurityBehavior
クラスはIClientMessageInspector
インターフェイスを実装するため、AfterReceiveReply()
とBeforeSendRequest()
の 2 つのメソッドを実装する必要があります。 このシナリオでは、AfterReceiverReply()
メソッドの一部として何もする必要はありません。 ただし、BeforeSendRequest()
メソッドはFetchOAuthToken()
メソッドを呼び出し、HttpPost()
メソッドを呼び出します。 その後、BeforeSendRequest()
メソッドは、承認トークンをメッセージ ヘッダーの一部として追加します。 また、 Salesforce から受信した応答が XML 形式であることを確認するために Accept ヘッダーも追加します。IEndpointBehavior
実装では、クライアント エンドポイントの動作にメッセージ インスペクター クラスを追加するだけです。注意事項
この例またはガイダンスでは、接続文字列やユーザー名とパスワードなどの機密情報を参照します。 これらの値をコードにハードコーディングしないでください。また、使用可能な最も安全な認証を使用して機密データを保護してください。 詳しくは、次のドキュメントをご覧ください。
public class SalesforceRESTSecurityBehavior : IClientMessageInspector, IEndpointBehavior { // Some constants private static string SalesforceAuthEndpoint = "https://login.salesforce.com/services/oauth2/token"; // Configuration Properties private string username_; private string password_; private string consumerKey_; private string consumerSecret_; private int sessionTimeout_; // Private Properties private SalesforceOAuthToken token_; private DateTime tokenExpiryTime_; public SalesforceRESTSecurityBehavior( string username, string password, string consumerKey, string consumerSecret, int sessionTimeout) { this.username_ = username; this.password_ = password; this.consumerKey_ = consumerKey; this.consumerSecret_ = consumerSecret; this.sessionTimeout_ = sessionTimeout; } private void FetchOAuthToken() { if ((tokenExpiryTime_ == null) || (tokenExpiryTime_.CompareTo(DateTime.Now) <= 0)) { StringBuilder body = new StringBuilder(); body.Append("grant_type=password&") .Append("client_id=" + consumerKey_ + "&") .Append("client_secret=" + consumerSecret_ + "&") .Append("username=" + username_ + "&") .Append("password=" + password_); string result; try { result = HttpPost(SalesforceAuthEndpoint, body.ToString()); } catch (WebException) { // do something return; } // Convert the JSON response into a token object JavaScriptSerializer ser = new JavaScriptSerializer(); this.token_ = ser.Deserialize<SalesforceOAuthToken>(result); this.tokenExpiryTime_ = DateTime.Now.AddSeconds(this.sessionTimeout_); } } public string HttpPost(string URI, string Parameters) { WebRequest req = WebRequest.Create(URI); req.ContentType = "application/x-www-form-urlencoded"; req.Method = "POST"; // Add parameters to post byte[] data = Encoding.ASCII.GetBytes(Parameters); req.ContentLength = data.Length; System.IO.Stream os = req.GetRequestStream(); os.Write(data, 0, data.Length); os.Close(); // Do the post and get the response. System.Net.WebResponse resp = null; resp = req.GetResponse(); StreamReader sr = new StreamReader(resp.GetResponseStream()); return sr.ReadToEnd().Trim(); } #region IClientMessageInspector public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { // do nothing } public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { // We are going to send a request to Salesforce // Overview: // This behavior will do the following: // (1) Fetch Token from Salesforce, if required // (2) Add the token to the message // (3) Insert an Http Accept header so we get XML data in response, instead of JSON, which is default // Reference: http://www.salesforce.com/us/developer/docs/api_rest/index.htm // // (1) Authentication with Salesforce // (2) The token is added to the HTTP Authorization Header // (3) Add the Accept Header // HttpRequestMessageProperty httpRequest = null; if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) { httpRequest = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; } if (httpRequest == null) { // NOTE: Ideally, we shouldn’t get here for WebHttp httpRequest = new HttpRequestMessageProperty() { Method = "GET", SuppressEntityBody = true }; request.Properties.Add(HttpRequestMessageProperty.Name, httpRequest); } WebHeaderCollection headers = httpRequest.Headers; FetchOAuthToken(); headers.Add(HttpRequestHeader.Authorization, "OAuth " + this.token_.access_token); headers.Add(HttpRequestHeader.Accept, "application/xml"); return null; } #endregion IClientMessageInspector #region IEndpointBehavior public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { // do nothing } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(this); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // do nothing } public void Validate(ServiceEndpoint endpoint) { // do nothing } #endregion IEndpointBehavior }
前の手順では、クライアント エンドポイントにメッセージ インスペクターを適用する動作クラスを作成しました。 ここで、Salesforce に要求メッセージを送信する WCF-WebHttp アダプターの構成エクスペリエンスでこの動作を使用できるようにする必要があります。 この動作を構成で使用できるようにするには、次の 2 つの操作を行う必要があります。
から派生するクラスを作成します。
BehaviorExtensionElement
要素名を使用して、machine.config の <extensions>\<behaviorExtensions> 要素に BehavaiorExtensionElement を登録します。
また、WCF-WebHttp アダプター構成 UI から使用できるように、このクラスに構成プロパティを追加します。
public class SalesforceRESTBehaviorElement : BehaviorExtensionElement { public override Type BehaviorType { get { return typeof(SalesforceRESTSecurityBehavior); } } protected override object CreateBehavior() { return new SalesforceRESTSecurityBehavior(Username, Password, ConsumerKey, ConsumerSecret, SessionTimeout); } [ConfigurationProperty("username", IsRequired = true)] public string Username { get { return (string)this["username"]; } set { this["username"] = value; } } [ConfigurationProperty("password", IsRequired = true)] public string Password { get { return (string)this["password"]; } set { this["password"] = value; } } [ConfigurationProperty("consumerKey", IsRequired = true)] public string ConsumerKey { get { return (string)this["consumerKey"]; } set { this["consumerKey"] = value; } } [ConfigurationProperty("consumerSecret", IsRequired = true)] public string ConsumerSecret { get { return (string)this["consumerSecret"]; } set { this["consumerSecret"] = value; } } [ConfigurationProperty("sessionTimeout", IsRequired = false, DefaultValue = 300)] public int SessionTimeout { get { return (int)this["sessionTimeout"]; } set { this["sessionTimeout"] = value; } } }
プロジェクトに厳密な名前キー ファイルを追加し、プロジェクトをビルドします。 プロジェクトが正常にビルドされたら、アセンブリ
Microsoft.BizTalk.Adapter.Behaviors
を GAC に追加します。System.ServiceModel\Extensions\BehaviorExtensions の下の machine.config に次のエントリを追加します。
<add name="Microsoft.BizTalk.Adapter.Behaviors.Demo.Salesforce" type="Microsoft.BizTalk.Adapter.Behaviors.SalesforceRESTBehaviorElement, Microsoft.BizTalk.Adapter.Behaviors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=45bd7fe67ef032db"/>
アセンブリの公開キー トークンは GAC から取得できます。 また、64 ビット コンピューターでこのアプリケーションを作成する場合は、このエントリを 32 ビットバージョンと 64 ビット バージョンの両方の machine.configに追加する必要があります。
Salesforce 応答に名前空間を追加するカスタム パイプラインを追加する
Salesforce から受信した応答には、名前空間は含まれません。 ただし、応答スキーマ (QueryResult.xsd) に対して応答メッセージを処理するには、Salesforce 応答に名前空間を含める必要があります。 このセクションでは、カスタム パイプラインを作成し、Microsoft BizTalk ESB Toolkit に付属するカスタム パイプライン コンポーネントを使用して、応答メッセージに名前空間を追加します。 このカスタム パイプラインは BizTalk Server アプリケーションと共に展開され、WCF-WebHttp アダプターの構成時に受信パイプラインとして使用されます。
この手順の手順を実行する前に、このアプリケーションを作成するコンピューターに Microsoft BizTalk ESB Toolkit がインストールされ、構成されていることを確認します。
カスタム パイプラインを追加するには
BtsSalesforceIntegration ソリューション内に、新しい BizTalk Server プロジェクトを作成します。 プロジェクトに
CustomPipeline
という名前を付けます。CustomPipeline プロジェクト内で、新しい受信パイプラインを追加します。 パイプラインに
AddNamespace.btp
という名前を付けます。パイプラインの デコード ステージ内で、ツールボックスから ESB 名前空間の追加 パイプライン コンポーネントをドラッグ アンド ドロップします。 逆アセンブル ステージ内で、XML 逆アセンブラー パイプライン コンポーネントをドラッグ アンド ドロップします。
注
ESB 名前空間の追加パイプライン コンポーネントがツールボックスに一覧表示されていない場合は、追加できます。 [BizTalk パイプライン コンポーネント] タブを右クリックし、[項目の選択] をクリックします。 [ ツールボックス項目の選択 ] ダイアログ ボックスで、[ BizTalk パイプライン コンポーネント ] タブをクリックし、[ ESB 名前空間 コンポーネントの追加] のチェック ボックスをオンにして、[ OK] をクリックします。
プロジェクトに厳密な名前キー ファイルを追加し、プロジェクトを保存します。
CustomPipeline プロジェクトを右クリックし、[プロパティ] を選択します。 [ 展開 ] タブの [ アプリケーション名] に「
SalesforceIntegration
」と入力します。プロジェクトへの変更を保存します。
このトピックでは、Salesforce で認証するカスタム動作と、Salesforce 応答に名前空間を追加するカスタム パイプラインを追加しました。 BizTalk Server 管理コンソールで物理ポートを構成するときに、これらのカスタム コンポーネントを使用します。