步驟 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 格式傳送訊息。
建立端點行為,以將訊息偵測器套用至用戶端端點。
建立行為延伸專案,以啟用端點行為的設定。
建立訊息偵測器
在 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
。 這個類別包含 和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 }
在上一個步驟中,我們建立了將訊息偵測器套用至用戶端端點的行為類別。 我們現在需要讓此行為可供將要求訊息傳送至 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; } } }
將強式名稱金鑰檔新增至專案並建置專案。 成功建置專案之後,請將元件
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 工具組的自訂管線元件,將命名空間新增至回應訊息。 此自訂管線會與BizTalk Server應用程式一起部署,並在設定 WCF-WebHttp 配接器時作為接收管線使用。
在執行此程式中的步驟之前,請確定您已在建立此應用程式的電腦上安裝及設定 Microsoft BizTalk ESB 工具組。
新增自訂管線
在BtsSalesforceIntegration解決方案中,建立新的BizTalk Server專案。 將專案命名為
CustomPipeline
。在 CustomPipeline 專案中,新增接收管線。 將管線命名為
AddNamespace.btp
。在管線的 解碼 階段內,從工具箱拖放 ESB 新增命名空間 管線元件。 在 反組譯 階段內,拖放 XML 反組譯程式 管線元件。
注意
如果 ESB 新增命名空間 管線元件未列在工具箱中,您可以新增它。 以滑鼠右鍵按一下 [BizTalk 管線元件 ] 索引標籤,然後按一下 [ 選擇專案]。 在 [ 選擇工具箱專案] 對話方塊中,按一下 [BizTalk 管線元件 ] 索引標籤,選取 [ESB 新增命名空間 ] 元件的核取方塊,然後按一下 [ 確定]。
將強式名稱金鑰檔案新增至專案並儲存專案。
以滑鼠右鍵按一下 CustomPipeline 專案,然後選取 [ 屬性]。 在 [ 部署] 索引標籤的 [ 應用程式名稱]中,輸入
SalesforceIntegration
。儲存專案的變更。
在本主題中,新增了自訂行為,以向 Salesforce 進行驗證,以及將命名空間新增至 Salesforce 回應的自訂管線。 我們會在 BizTalk Server 管理主控台中設定實體埠時使用這些自訂群組件。