次の方法で共有


手順 3d: Salesforce からメッセージを送受信するBizTalk Serverを有効にする

REST インターフェイスを使用してメッセージを送信する際は、Salesforce で認証する必要があります。 Salesforce でサポートされている REST 呼び出しのための認証方法は、Salesforce の REST インターフェイスの起動に使用する WCF-WebHttp アダプターの既定では利用できません。 そのため、カスタムの 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 に送信されるクエリ メッセージの認証ヘッダーの値として認証トークンを追加します。 さらに、メッセージ インスペクタは、受信した応答が XML 形式になるように、要求メッセージに Accept ヘッダーを追加します。 それ以外の場合は、Salesforce は既定の JSON 形式でメッセージを送信します。

  • メッセージ インスペクタをクライアント エンドポイントに適用するためのエンドポイントの動作を作成します。

  • エンドポイントの動作構成を可能にする動作拡張機能の要素を作成します。

メッセージ インスペクタを作成するには

  1. 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;
    
  2. ソリューション エクスプローラーから、、、 System.Configurationへの参照をSystem.ServiceModelSystem.Web.Extensions追加します。

  3. 次のプロパティを持つクラス 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; }
    }
    
  4. と を実装するIClientMessageInspectorクラスSalesforceRESTSecurityBehaviorを追加しますIEndpointBehavior。 このクラスには、 HttpPost() Salesforce 認証エンドポイントに対して HTTP POST 呼び出しを行って承認トークンを取得する メソッドと FetchOAuthToken() メソッドが含まれています。

    クラスは SalesforceRESTSecurityBehavior インターフェイスをIClientMessageInspector実装するため、2 つのメソッド と BeforeSendRequest()を実装するAfterReceiveReply()必要があります。 このシナリオでは、 メソッドの AfterReceiverReply() 一部として何も行う必要はありません。 ただし、 メソッドは BeforeSendRequest() メソッドを FetchOAuthToken() 呼び出し、 メソッドを HttpPost() 呼び出します。 次に、 メソッドは BeforeSendRequest() 、メッセージ ヘッダーの一部として承認トークンを追加します。 また、 Accept ヘッダーを追加して、Salesforce から受信した応答が XML 形式であることを確認します。

    実装では 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
    }
    
  5. 前の手順では、クライアント エンドポイントにメッセージ インスペクタを適用する動作のクラスを作成しました。 次に、この動作で 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; }
        }
    }
    
  6. 厳密な名前のキー ファイルをプロジェクトに追加し、プロジェクトを構築します。 プロジェクトが正常にビルドされたら、アセンブリ Microsoft.BizTalk.Adapter.Behaviors を GAC に追加します。

  7. 次のエントリを、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 ビット コンピューターで作成する場合は、machine.config の 32 ビットおよび 64 ビット バージョンの両方にこのエントリを追加する必要があります。

Salesforce 応答に名前空間を追加するためのカスタム パイプラインを追加する

Salesforce から受け取る応答には名前空間が含まれません。 ただし、応答スキーマ (QueryResult.xsd) に対して応答メッセージを処理するには、Salesforce 応答に名前空間を含める必要があります。 このセクションでは、カスタム パイプラインを作成し、Microsoft BizTalk ESB Toolkit に付属しているカスタム パイプライン コンポーネントを使用して、応答メッセージに名前空間を追加します。 このカスタム パイプラインは、BizTalk Server アプリケーションと共にデプロイされ、WCF-WebHttp アダプターの構成中に受信パイプラインとして使用されます。

この手順の手順を実行する前に、このアプリケーションを作成するコンピューターに Microsoft BizTalk ESB Toolkit がインストールされ、構成されていることを確認します。

カスタム パイプラインを追加するには

  1. BtsSalesforceIntegration ソリューション内で、新しいBizTalk Server プロジェクトを作成します。 プロジェクトに CustomPipeline という名前を付けます。

  2. CustomPipeline プロジェクト内に、新しい受信パイプラインを追加します。 パイプラインに という名前を付けます AddNamespace.btp

  3. パイプラインの Decode ステージ内で、ツールボックスから ESB 名前空間の追加パイプライン コンポーネントをドラッグ アンド ドロップします。 逆アセンブリ ステージ内で、XML 逆アセンブラー パイプライン コンポーネントをドラッグ アンド ドロップします。

    Note

    ESB 名前空間の追加パイプライン コンポーネントがツールボックスに一覧表示されていない場合は、追加できます。 [BizTalk パイプライン コンポーネント] タブを右クリックし、[項目の選択] をクリックします。 [ツールボックス 項目の選択] ダイアログ ボックスで、[BizTalk パイプライン コンポーネント] タブをクリックし、[ESB 名前空間の追加] コンポーネントの [チェック] ボックスを選択し、[OK] をクリックします

  4. 厳密な名前のキー ファイルをプロジェクトに追加し、プロジェクトを保存します。

  5. CustomPipeline プロジェクトを右クリックし、[プロパティ] を選択します。 [展開] タブの [アプリケーション名] に「」と入力しますSalesforceIntegration

  6. プロジェクトへの変更内容を保存します。

    このトピックでは、Salesforce で認証するためのカスタム動作を追加し、Salesforce 応答に名前空間を追加するためのカスタム パイプラインを追加しました。 管理コンソールで物理ポートを構成するときに、これらのカスタム コンポーネントBizTalk Server使用します。

参照

ステップ 3:Visual Studio での BizTalk Server ソリューションの作成