共用方式為


步驟 3d:啟用BizTalk Server從 Salesforce 傳送和接收訊息

我們必須在使用 REST 介面傳送訊息時向 Salesforce 進行驗證。 Salesforce 支援的 REST 呼叫驗證方法目前無法使用 WCF-WebHttp 配接器,我們將用來叫用 Salesforce 的 REST 介面。 因此,我們將建立自訂 WCF 端點行為,然後將它附加至 WCF-WebHttp 傳送配接器,我們將設定為叫用 Salesforce REST 介面。

同樣地,從 Salesforce 收到的 XML 回應將不會包含任何命名空間。 若要BizTalk Server針對回應架構處理回應訊息,回應訊息必須包含目標命名空間。 因此,我們將使用 Microsoft BizTalk ESB 工具組建立自訂管線,以將命名空間新增至回應訊息。 然後,我們將使用此自訂管線作為要求-回應的接收管線,WCF-WebHttp 傳送埠。

新增 Salesforce 驗證的自訂 WCF 行為

Salesforce 為用戶端應用程式提供不同的選項,以向 Salesforce 進行驗證。 我們將使用 Salesforce 詞彙作為 Username-password 流程。 在用戶端上,WCF 可讓您建立 訊息偵測器 ,以在訊息傳送之前或接收訊息之後變更訊息。 訊息偵測器是用戶端執行時間的延伸模組,並設定為行為。 接著,行為會新增至行為延伸專案。 此行為延伸專案會在 machine.config 中註冊,其專案名稱可讓您在 WCF 埠上設定為自訂行為。 如需詳細資訊,請參閱 使用自訂行為擴充 WCF。 我們將使用這種方法來納入使用者名稱驗證流程,以向 Salesforce 進行驗證。 我們將遵循的廣泛步驟如下:

  • 建立訊息偵測器,以對 Salesforce 驗證端點進行 HTTP POST 呼叫,並接收存取權杖。 然後,訊息偵測器會將驗證權杖新增為傳送至 Salesforce 之查詢訊息的 Authorization 標頭值。 訊息偵測器也會將 Accept 標頭新增至要求訊息,讓收到的回應採用 XML 格式。 否則,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.ServiceModelSystem.Web.ExtensionsSystem.Configuration

  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 和 的 IEndpointBehavior 類別 SalesforceRESTSecurityBehavior 。 這個類別包含 和 HttpPost()FetchOAuthToken() 方法,這些方法會呼叫 Salesforce 驗證端點以擷取授權權杖。

    SalesforceRESTSecurityBehavior因為 類別會實作 IClientMessageInspector 介面,所以它必須實作兩種方法和 AfterReceiveReply()BeforeSendRequest() 。 在此案例中,我們不需要在 方法中 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 配接器設定體驗使用。 若要讓此行為可供設定使用,我們需要執行兩件事:

    • 建立衍生自 的類別 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 位電腦上建立此應用程式,則必須將此專案同時新增至 32 位和 64 位版本的 machine.config。

新增自訂管線以將命名空間新增至 Salesforce 回應

從 Salesforce 收到的回應不包含命名空間。 不過,若要針對回應架構處理回應訊息, (QueryResult.xsd) 我們必須在 Salesforce 回應中包含 命名空間。 在本節中,我們會建立自訂管線,並使用隨附于 Microsoft BizTalk ESB 工具組的自訂管線元件,將命名空間新增至回應訊息。 此自訂管線會與BizTalk Server應用程式一起部署,並在設定 WCF-WebHttp 配接器時作為接收管線使用。

在執行此程式中的步驟之前,請確定您已在建立此應用程式的電腦上安裝及設定 Microsoft BizTalk ESB 工具組。

新增自訂管線

  1. BtsSalesforceIntegration解決方案中,建立新的BizTalk Server專案。 將專案命名為 CustomPipeline

  2. CustomPipeline 專案中,新增接收管線。 將管線命名為 AddNamespace.btp

  3. 在管線的 解碼 階段內,從工具箱拖放 ESB 新增命名空間 管線元件。 在 反組譯 階段內,拖放 XML 反組譯程式 管線元件。

    注意

    如果 ESB 新增命名空間 管線元件未列在工具箱中,您可以新增它。 以滑鼠右鍵按一下 [BizTalk 管線元件 ] 索引標籤,然後按一下 [ 選擇專案]。 在 [ 選擇工具箱專案] 對話方塊中,按一下 [BizTalk 管線元件 ] 索引標籤,選取 [ESB 新增命名空間 ] 元件的核取方塊,然後按一下 [ 確定]。

  4. 將強式名稱金鑰檔案新增至專案並儲存專案。

  5. 以滑鼠右鍵按一下 CustomPipeline 專案,然後選取 [ 屬性]。 在 [ 部署] 索引標籤的 [ 應用程式名稱]中,輸入 SalesforceIntegration

  6. 儲存專案的變更。

    在本主題中,新增了自訂行為,以向 Salesforce 進行驗證,以及將命名空間新增至 Salesforce 回應的自訂管線。 我們會在 BizTalk Server 管理主控台中設定實體埠時使用這些自訂群組件。

另請參閱

步驟 3:在 Visual Studio 中建立 BizTalk Server 解決方案