Partager via


Étape 3d : Activation de BizTalk Server pour envoyer et recevoir des messages de Salesforce

Nous devons nous authentifier auprès de Salesforce lors l'envoi de messages à l'aide de l'interface REST. Les méthodes d'authentification pour les appels REST pris en charge par Salesforce ne sont pas immédiatement prêts à l'emploi avec l'adaptateur WCF-WebHtt, que nous allons utiliser pour appeler l'interface REST de Salesforce. Ainsi, nous allons créer un comportement de point de terminaison WCF personnalisé, puis l'attacher à l'adaptateur d'envoi WCF-WebHttp que nous allons configurer pour appeler l'interface REST de Salesforce.

De même, la réponse XML reçue de Salesforce ne contiendra aucun espace de noms. Pour BizTalk Server de traiter le message de réponse par rapport au schéma de réponse, le message de réponse doit inclure l’espace de noms cible. Nous allons donc créer un pipeline personnalisé à l’aide de Microsoft BizTalk ESB Toolkit pour ajouter un espace de noms au message de réponse. Nous utiliserons ensuite ce pipeline personnalisé comme pipeline de réception pour le port d'envoi WCF-WebHttp requête-réponse.

Ajouter un comportement WCF personnalisé pour l'authentification Salesforce

Salesforce propose différentes options pour permettre aux applications clientes de s'authentifier auprès de Salesforce. Nous allons utiliser les termes salesforce comme flux nom d’utilisateur-mot de passe . Côté client, WCF vous permet de créer des inspecteurs de messages pour modifier les messages avant leur envoi ou après leur réception. Un inspecteur de message est une extension du runtime client et est configuré comme un comportement. Un comportement est, à son tour, ajouté à un élément d'extension de comportement. Cet élément d'extension de comportement est inscrit dans machine.config avec un nom d'élément, ce qui le rend disponible pour être configuré comme comportement personnalisé sur un port WCF. Pour plus d’informations, consultez Extension de WCF avec des comportements personnalisés. Nous allons utiliser cette approche pour incorporer le flux nom d'utilisateur-authentification pour nous authentifier auprès de Salesforce. Les grandes étapes que nous allons suivre sont les suivantes :

  • Créer un inspecteur de message qui lance un appel HTTP POST au point de terminaison d'authentification Salesforce et reçoit un jeton d'accès. L'inspecteur de message ajoute ensuite le jeton d'authentification comme valeur d'en-tête d'autorisation du message de requête envoyé à Salesforce. L'inspecteur de message ajoute également l'en-tête Accepter pour le message de la requête de sorte que la réponse reçue soit au format XML. Dans le cas contraire, Salesforce envoie le message au format JSON par défaut.

  • Créer un comportement de point de terminaison pour appliquer l'inspecteur de message au point de terminaison client.

  • Créer un élément d'extension de comportement pour activer la configuration du comportement du point de terminaison.

Pour créer un inspecteur de message

  1. Dans le cadre de la solution BtsSalesforceIntegration dans Visual Studio, créez une bibliothèque de classes C#. Nommez-le Microsoft.BizTalk.Adapter.Behaviors et ajoutez les espaces de noms suivants :

    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. À partir de la Explorateur de solutions, ajoutez des références à System.ServiceModel, System.Web.Extensionset System.Configuration.

  3. Ajoutez une classe SalesforceOAuthToken avec les propriétés suivantes. Cette classe représente le jeton d'autorisation qui est reçu de 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. Ajoutez une classe SalesforceRESTSecurityBehavior qui implémente le IClientMessageInspector et .IEndpointBehavior Cette classe inclut les HttpPost() méthodes et FetchOAuthToken() qui effectuent un appel HTTP POST au point de terminaison d’authentification Salesforce pour récupérer le jeton d’autorisation.

    Étant donné que la SalesforceRESTSecurityBehavior classe implémente l’interface IClientMessageInspector , elle doit implémenter les deux méthodes et AfterReceiveReply()BeforeSendRequest(). Pour ce scénario, nous n’avons rien à faire dans le cadre de la AfterReceiverReply() méthode . Toutefois, la BeforeSendRequest() méthode appelle la FetchOAuthToken() méthode , qui à son tour appelle la HttpPost() méthode . La BeforeSendRequest() méthode ajoute ensuite le jeton d’autorisation dans le cadre de l’en-tête de message. Il ajoute également l’en-tête Accepter pour s’assurer que la réponse reçue de Salesforce est au format XML.

    L’implémentation IEndpointBehavior ajoute simplement la classe d’inspecteur de message au comportement du point de terminaison client.

    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. Dans l'étape précédente, nous avons créé une classe de comportement qui applique l'inspecteur de message au point de terminaison client. Nous devons maintenant mettre ce comportement à la disposition de l'expérience de la configuration pour l'adaptateur WCF-WebHttp qui enverra le message de la requête à Salesforce. Pour rendre ce comportement disponible pour la configuration, nous devons faire deux choses :

    • Créer une classe qui dérive de BehaviorExtensionElement

    • Inscrivez votre BehavaiorExtensionElement dans l’élément <extensions>\<behaviorExtensions> dans le machine.config à l’aide d’un nom d’élément.

      Nous allons également ajouter des propriétés de configuration à cette classe afin qu'elles soient disponibles à partir de l'interface utilisateur de configuration de l'adaptateur WCF-WebHttp.

    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. Ajoutez un fichier de clé de nom fort pour le projet et construisez le projet. Une fois le projet généré avec succès, ajoutez l’assembly Microsoft.BizTalk.Adapter.Behaviors au GAC.

  7. Ajoutez l'entrée suivante dans machine.config sous System.ServiceModel\Extensions\BehaviorExtensions.

    <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"/>
    

    Vous pouvez récupérer le jeton de clé publique pour l'assembly à partir du GAC. En outre, si vous créez cette application sur un ordinateur 64 bits, vous devez ajouter cette entrée à la fois aux versions 32 bits et 64 bits de machine.config.

Ajouter un pipeline personnalisé pour ajouter un espace de noms à la réponse de Salesforce

La réponse reçue de Salesforce ne comprend pas d'espace de noms. Toutefois, pour traiter le message de réponse par rapport au schéma de réponse (QueryResult.xsd) nous devons inclure l'espace de noms dans la réponse Salesforce. Dans cette section, nous créons un pipeline personnalisé et utilisons un composant de pipeline personnalisé fourni avec Microsoft BizTalk ESB Toolkit pour ajouter un espace de noms au message de réponse. Ce pipeline personnalisé est déployé avec l’application BizTalk Server et sera utilisé comme pipeline de réception lors de la configuration de l’adaptateur WCF-WebHttp.

Avant d’effectuer les étapes de cette procédure, vérifiez que Microsoft BizTalk ESB Toolkit est installé et configuré sur l’ordinateur sur lequel vous créez cette application.

Pour ajouter un pipeline personnalisé

  1. Dans la solution BtsSalesforceIntegration, créez un projet BizTalk Server. Nommez le projet CustomPipeline.

  2. Dans le projet CustomPipeline , ajoutez un nouveau pipeline de réception. Nommez le pipeline en tant que AddNamespace.btp.

  3. Dans la phase Decode du pipeline, à partir de la boîte à outils, faites glisser et déposez le composant de pipeline d’ajout d’espace de noms ESB . Dans la phase Désassembler , faites glisser et déposez le composant de pipeline de désassembleur XML .

    Notes

    Si le composant de pipeline d’ajout d’espace de noms ESB n’est pas répertorié dans la boîte à outils, vous pouvez l’ajouter. Cliquez avec le bouton droit sur l’onglet Composants de pipeline BizTalk , puis cliquez sur Choisir des éléments. Dans la boîte de dialogue Choisir des éléments de boîte à outils, cliquez sur l’onglet Composants de pipeline BizTalk, sélectionnez la zone case activée pour le composant Esb Add Namespace, puis cliquez sur OK.

  4. Ajoutez un fichier de clé de nom fort pour le projet et enregistrez le projet.

  5. Cliquez avec le bouton droit sur le projet CustomPipeline et sélectionnez Propriétés. Sous l’onglet Déploiement , pour Nom de l’application, entrez SalesforceIntegration.

  6. Enregistrez les changements apportés au projet.

    Dans cette rubrique, vous avez ajouté un comportement personnalisé pour vous authentifier auprès de Salesforce et un pipeline personnalisé pour ajouter un espace de noms à la réponse de Salesforce. Nous allons utiliser ces composants personnalisés lors de la configuration des ports physiques dans BizTalk Server console d’administration.

Voir aussi

Étape 3 : créer la solution BizTalk Server dans Visual Studio